From 83bc1298c75afe997c599d2449ffa86707da55d8 Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Wed, 7 Aug 2024 15:53:43 +0800
Subject: [PATCH 01/28] feat: perfect isTrackable funtion
---
src/adapter/adapter-base.ts | 6 ++-
src/core/track-builder.ts | 9 +++-
src/helpers/helper-select-adapter.ts | 10 +++-
src/types/types-adapter.ts | 9 +++-
tests/fixtures/adapter/report-adapter.ts | 4 +-
tests/test-track-execute-select.spec.ts | 61 ++++++++++++++++++------
6 files changed, 79 insertions(+), 20 deletions(-)
diff --git a/src/adapter/adapter-base.ts b/src/adapter/adapter-base.ts
index f0c9858..4d2bfc7 100644
--- a/src/adapter/adapter-base.ts
+++ b/src/adapter/adapter-base.ts
@@ -24,7 +24,11 @@ export abstract class BaseAdapter<
private afterHook?: AdapterAfterFunction;
- abstract isTrackable(): boolean | Promise;
+ abstract isTrackable(
+ ctx: Context,
+ eventType: EventType,
+ eventData: EventData[EventType]
+ ): boolean | Promise;
protected report(
ctx: Context,
diff --git a/src/core/track-builder.ts b/src/core/track-builder.ts
index fd46ee6..01c8915 100644
--- a/src/core/track-builder.ts
+++ b/src/core/track-builder.ts
@@ -150,9 +150,16 @@ export class TrackBuilder<
async ({ trackCtx }) => {
const adapterMap = await executeSelect<
Context,
+ EventType,
EventData,
AdapterMap
- >(trackCtx, innerTrackAdapterMap as AdapterMap, selectRule);
+ >(
+ trackCtx,
+ eventType,
+ eventData,
+ innerTrackAdapterMap as AdapterMap,
+ selectRule
+ );
return { trackCtx, adapterMap };
},
async ({ trackCtx, adapterMap }) => {
diff --git a/src/helpers/helper-select-adapter.ts b/src/helpers/helper-select-adapter.ts
index 56edea1..67b6701 100644
--- a/src/helpers/helper-select-adapter.ts
+++ b/src/helpers/helper-select-adapter.ts
@@ -18,10 +18,13 @@ import { isFunction } from './helper-is-function.js';
*/
export const executeSelect = async <
Context extends TrackContext,
+ EventType extends keyof EventData,
EventData extends TrackEventDataBase,
TrackMap extends TrackAdapterMap,
>(
ctx: Context,
+ eventType: EventType,
+ eventData: EventData[EventType],
adapterMap: TrackMap,
selectRule: TrackSelectFunction = []
): Promise> => {
@@ -43,7 +46,12 @@ export const executeSelect = async <
const lasterAdapterMap: TrackAdapterMap = {};
for (const [adapterName, adapter] of Object.entries(adapterMap)) {
- const isTrackable = await executeFunction(adapter.isTrackable);
+ const isTrackable = await executeFunction(
+ adapter.isTrackable,
+ ctx,
+ eventType,
+ eventData
+ );
if (isTrackable && names.includes(adapterName)) {
lasterAdapterMap[adapterName] = adapterMap[adapterName];
}
diff --git a/src/types/types-adapter.ts b/src/types/types-adapter.ts
index aa04d19..4a3e8e5 100644
--- a/src/types/types-adapter.ts
+++ b/src/types/types-adapter.ts
@@ -81,9 +81,16 @@ export interface TrackAdapter<
/**
* Checks if the adapter is available.
+ * @param ctx The track context.
+ * @param eventType The type of the event.
+ * @param eventData The data associated with the event.
* @returns A boolean indicating if the adapter is available.
*/
- isTrackable(): boolean | Promise;
+ isTrackable(
+ ctx: Context,
+ eventType: EventType,
+ eventData: EventData[EventType]
+ ): boolean | Promise;
/**
* Tracks an event.
diff --git a/tests/fixtures/adapter/report-adapter.ts b/tests/fixtures/adapter/report-adapter.ts
index ed08c65..c9bffa6 100644
--- a/tests/fixtures/adapter/report-adapter.ts
+++ b/tests/fixtures/adapter/report-adapter.ts
@@ -10,10 +10,10 @@ export class ReportAdapter extends BaseAdapter<
EventDataOption,
AdapterOptions, EventDataOption>
> {
- isTrackable(): boolean | Promise {
+
+ isTrackable(ctx: TrackContext, eventType: EventType, eventData: EventDataOption[EventType]): boolean | Promise {
return true;
}
-
report(
ctx: TrackContext,
reportData: AdapterReportData,
diff --git a/tests/test-track-execute-select.spec.ts b/tests/test-track-execute-select.spec.ts
index 454a77f..e92a5b6 100644
--- a/tests/test-track-execute-select.spec.ts
+++ b/tests/test-track-execute-select.spec.ts
@@ -23,7 +23,13 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(ctx, adapterMap, undefined);
+ const lastAdapterMap = await executeSelect(
+ ctx,
+ 'addCart',
+ {},
+ adapterMap,
+ undefined
+ );
expect(Object.keys(lastAdapterMap).length).toBe(5);
expect(lastAdapterMap).toMatchObject({
@@ -47,6 +53,8 @@ describe('test-track-execute-select.spec', () => {
};
const lastAdapterMap = await executeSelect(
ctx,
+ 'addCart',
+ {},
adapterMap,
'consoleAdapter'
);
@@ -67,7 +75,7 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(ctx, adapterMap, [
+ const lastAdapterMap = await executeSelect(ctx, 'addCart', {}, adapterMap, [
'consoleAdapter',
'analyzerAdapter',
'reportAdapter',
@@ -92,6 +100,8 @@ describe('test-track-execute-select.spec', () => {
};
const lastAdapterMap = await executeSelect(
ctx,
+ 'addCart',
+ {},
adapterMap,
() => 'consoleAdapter'
);
@@ -112,11 +122,13 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(ctx, adapterMap, () => [
- 'consoleAdapter',
- 'analyzerAdapter',
- 'reportAdapter',
- ]);
+ const lastAdapterMap = await executeSelect(
+ ctx,
+ 'addCart',
+ {},
+ adapterMap,
+ () => ['consoleAdapter', 'analyzerAdapter', 'reportAdapter']
+ );
expect(Object.keys(lastAdapterMap).length).toBe(3);
expect(lastAdapterMap).toMatchObject({
@@ -136,8 +148,12 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(ctx, adapterMap, () =>
- Promise.resolve('analyzerAdapter')
+ const lastAdapterMap = await executeSelect(
+ ctx,
+ 'addCart',
+ {},
+ adapterMap,
+ () => Promise.resolve('analyzerAdapter')
);
expect(Object.keys(lastAdapterMap).length).toBe(1);
@@ -156,8 +172,13 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(ctx, adapterMap, () =>
- Promise.resolve(['businessAdapter', 'analyzerAdapter', 'logAdapter'])
+ const lastAdapterMap = await executeSelect(
+ ctx,
+ 'addCart',
+ {},
+ adapterMap,
+ () =>
+ Promise.resolve(['businessAdapter', 'analyzerAdapter', 'logAdapter'])
);
expect(Object.keys(lastAdapterMap).length).toBe(3);
@@ -188,7 +209,13 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter4,
businessAdapter: adapter5,
};
- let lastAdapterMap = await executeSelect(ctx, adapterMap, undefined);
+ let lastAdapterMap = await executeSelect(
+ ctx,
+ 'addCart',
+ {},
+ adapterMap,
+ undefined
+ );
expect(Object.keys(lastAdapterMap).length).toBe(5);
expect(lastAdapterMap).toMatchObject({
@@ -212,7 +239,13 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter4,
businessAdapter: adapter5,
};
- lastAdapterMap = await executeSelect(ctx, adapterMap, undefined);
+ lastAdapterMap = await executeSelect(
+ ctx,
+ 'addCart',
+ {},
+ adapterMap,
+ undefined
+ );
expect(Object.keys(lastAdapterMap).length).toBe(1);
expect(lastAdapterMap).toMatchObject({
consoleAdapter: adapter1,
@@ -231,7 +264,7 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter4,
businessAdapter: adapter5,
};
- lastAdapterMap = await executeSelect(ctx, adapterMap, () => {
+ lastAdapterMap = await executeSelect(ctx, 'addCart', {}, adapterMap, () => {
return Promise.resolve(['reportAdapter']);
});
expect(Object.keys(lastAdapterMap).length).toBe(1);
From c8c602f8c02d551e64fd57ac83ca655462e13d10 Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Wed, 7 Aug 2024 21:24:13 +0800
Subject: [PATCH 02/28] =?UTF-8?q?feat:=20isTrackable=20add=20ctx=E3=80=81e?=
=?UTF-8?q?ventType=E3=80=81eventData?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
tests/test-adapter.spec.ts | 10 ++++++
tests/test-deep-merge.spec.ts | 35 ++++++++++++++++++++
tests/test-track-parallel-pipeline.spec.ts | 37 +++++++++++++++++++++-
vitest.config.ts | 6 ++++
4 files changed, 87 insertions(+), 1 deletion(-)
diff --git a/tests/test-adapter.spec.ts b/tests/test-adapter.spec.ts
index 8695edc..b9383c8 100644
--- a/tests/test-adapter.spec.ts
+++ b/tests/test-adapter.spec.ts
@@ -33,6 +33,16 @@ describe('test-adapter.spec', () => {
},
};
+ it('test adapter is undefined', async () => {
+ expect(() =>
+ createAdapterBuilder<
+ TrackContext,
+ EventDataOption,
+ AdapterOptions, EventDataOption>
+ >(undefined as unknown as ReportAdapter)
+ ).toThrowError('Adapter is required');
+ });
+
it('test adapter hook', async () => {
const reportAdapter = new ReportAdapter();
diff --git a/tests/test-deep-merge.spec.ts b/tests/test-deep-merge.spec.ts
index 9fcebcb..c883dbd 100644
--- a/tests/test-deep-merge.spec.ts
+++ b/tests/test-deep-merge.spec.ts
@@ -1,6 +1,41 @@
import { deepMerge } from '../src/helpers/helper-deep-merge.js';
+import { isObject } from '../src/index.js';
describe('test-deep-merge.spec', () => {
+ it('source is undefined', () => {
+ const nestedObject = { b: 2, y: 2 };
+ const input: any = {
+ a: nestedObject,
+ x: 1,
+ };
+
+ const result = deepMerge(input, undefined as any);
+ expect(result).toBe(input);
+ });
+
+ it('target key is not object', () => {
+ const nestedObject = {
+ b: 2,
+ y: 2,
+ x: {
+ xxx: 1,
+ },
+ };
+ const input: any = {
+ a: nestedObject,
+ x: ['a', 'b'],
+ };
+
+ const result = deepMerge(input, nestedObject as any);
+
+ expect(result).toMatchObject({
+ a: { b: 2, y: 2, x: { xxx: 1 } },
+ x: { xxx: 1 },
+ b: 2,
+ y: 2,
+ });
+ });
+
it('creates a new object reference', () => {
const nestedObject = { b: 2, y: 2 };
const input: any = {
diff --git a/tests/test-track-parallel-pipeline.spec.ts b/tests/test-track-parallel-pipeline.spec.ts
index 16418dc..bd76ba1 100644
--- a/tests/test-track-parallel-pipeline.spec.ts
+++ b/tests/test-track-parallel-pipeline.spec.ts
@@ -41,10 +41,20 @@ describe('test-track-pipeline.spec', () => {
consoleAdapter: consoleAdapter,
};
+ const createData: TrackData = {
+ bizMode: 'test',
+ env: 'prod',
+ platform: 'android',
+ ip: '0.0.0.0',
+ userId: 'test',
+ };
+
const trackBuilder = createTrackBuilder<
TrackContext,
EventDataOption
- >();
+ >({
+ createData,
+ });
const trackBuilderFactory = trackBuilder.init(() => adapterMap);
@@ -54,6 +64,10 @@ describe('test-track-pipeline.spec', () => {
await trackBuilderFactory
.select('analyzerAdapter')
.track('addCart', eventData.addCart);
+ // analyzerAdapter
+ expect(analyzerReportFun.mock.lastCall?.[0]?.data).toMatchObject(
+ createData
+ );
expect(analyzerReportCallback.mock.results[0].value).toBe(
'analyzerAdapter'
);
@@ -61,12 +75,24 @@ describe('test-track-pipeline.spec', () => {
setTimeout(async () => {
await trackBuilderFactory.select().track('addCart', eventData.addCart);
+ // reportAdapter
+ expect(reportReportFun.mock.lastCall?.[0]?.data).toMatchObject(
+ createData
+ );
expect(reportReportCallback.mock.results[0].value).toBe(
'reportAdapter'
);
+ // analyzerAdapter
+ expect(analyzerReportFun.mock.lastCall?.[0]?.data).toMatchObject(
+ createData
+ );
expect(analyzerReportCallback.mock.results[0].value).toBe(
'analyzerAdapter'
);
+ // consoleAdapter
+ expect(consoleReportFun.mock.lastCall?.[0]?.data).toMatchObject(
+ createData
+ );
expect(consoleReportCallback.mock.results[0].value).toBe(
'consoleAdapter'
);
@@ -78,9 +104,18 @@ describe('test-track-pipeline.spec', () => {
return ['consoleAdapter', 'analyzerAdapter'];
})
.track('addCart', eventData.addCart);
+
+ // analyzerAdapter
+ expect(analyzerReportFun.mock.lastCall?.[0]?.data).toMatchObject(
+ createData
+ );
expect(analyzerReportCallback.mock.results[0].value).toBe(
'analyzerAdapter'
);
+ // consoleAdapter
+ expect(consoleReportFun.mock.lastCall?.[0]?.data).toMatchObject(
+ createData
+ );
expect(consoleReportCallback.mock.results[0].value).toBe(
'consoleAdapter'
);
diff --git a/vitest.config.ts b/vitest.config.ts
index 2ae0a72..0267552 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -9,5 +9,11 @@ export default defineConfig({
'@/': new URL('./src/', import.meta.url).pathname,
},
include: ['**/?(*.){test,spec}.?(c|m)[jt]s?(x)'],
+ coverage: {
+ provider: 'istanbul',
+ reporter: ['html'],
+ include: ['src/**'],
+ enabled: true,
+ },
},
});
From de5701d1cba16dab7b41e3efa724fb97db193ca6 Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Thu, 8 Aug 2024 21:46:00 +0800
Subject: [PATCH 03/28] docs: perfect readme
---
README.md | 210 ++++++++++++++++++++++++++++++++++++------------------
1 file changed, 140 insertions(+), 70 deletions(-)
diff --git a/README.md b/README.md
index 0d2e6c4..ca509a4 100644
--- a/README.md
+++ b/README.md
@@ -17,11 +17,17 @@
A typed, smart, scalable , powerful data collection engine written in typescript
-## Usage
+## Install
-### Create TrackBuilder
+```ts
+// npm
+npm i @hyperse/track
-Create a builder to load the track
+// yarn
+yarn add @hyperse/track
+```
+
+## Usage
```ts
export type Context = {
@@ -46,87 +52,151 @@ export type EventData = {
};
};
-const trackBuilder = await createTrackBuilder({
- createCtx() {
- // Used to build a global context
- return Promise.resolve(context);
- },
- eventData: {
- // Generic EventData-type data is deeply merged during the transform phase
- },
- // The formatStrategy for logger
- formatStrategy: formatStrategy,
-});
-```
-
-### Create Adapter
-
-Create a adapter by createAdapterBuilder function
-
-```ts
-const adapterBuilder = await createAdapterBuilder();
+export type AdapterOptions = {
+ setup?: (
+ ctx: Context,
+ eventData: EventData[keyof EventData]
+ ) => Promise<{
+ name: 'setup' | 'setup1' | 'setup2';
+ timeStamp: number;
+ }>;
+};
-const adapter = await adapterBuilder
- .init(() => {
- // Initialization adapter
- })
- .before((ctx) => {
- // Execute before the adapter track function
- })
- .isTrackable(() => {
- // Determine whether the adapter is trackable
+// custom report adapter
+export class ReportAdapter extends BaseAdapter<
+ TrackContext,
+ EventData,
+ AdapterOptions, EventData>
+> {
+ isTrackable(
+ ctx: TrackContext,
+ eventType: EventType,
+ eventData: EventData[EventType]
+ ): boolean | Promise {
return true;
+ }
+ report(
+ ctx: TrackContext,
+ reportData: AdapterReportData,
+ setupData?:
+ | {
+ name: 'setup' | 'setup1' | 'setup2';
+ timeStamp: number;
+ }
+ | undefined
+ ): void | Promise {}
+}
+
+const reportAdapter = new ReportAdapter();
+
+// create adapter builder
+const adapterBuilder = createAdapterBuilder<
+ TrackContext,
+ EventData,
+ AdapterOptions, EventData>
+>(reportAdapter);
+
+// mount adapter hook
+adapterBuilder
+ .setup((ctx, eventData) => {
+ return Promise.resolve({
+ name: 'setup' as const,
+ timeStamp: new Date().getTime(),
+ newField: 'newField',
+ });
})
- .transform((ctx, eventType, eventData) => {
- // Transform the eventData
- return eventData;
+ .before(async (ctx, eventType, eventData) => {
+ console.log('before');
})
- .after((ctx) => {
- // Execute after the adapter track function
+ .transform('addCart', (ctx, eventType, eventData) => {
+ return {
+ ...eventData,
+ pay: {
+ payId: 'p123',
+ payName: 'Sample Pay',
+ payType: 'credit',
+ },
+ timeStamp: '2024-09-01T00:00:00Z',
+ };
+ })
+ .after(async (ctx, eventType, eventData) => {
+ console.log('after', eventData);
})
- // Return a adapter instance
.build();
+
+// create track builder
+const trackBuilder = createTrackBuilder<
+ TrackContext,
+ EventDataOption
+>();
+
+// mount track hook
+await trackBuilder
+ .init({ reportAdapter: reportAdapter })
+ .before(async (ctx) => {})
+ .after(async (ctx) => {})
+ .select(() => ['reportAdapter'])
+ .track('addCart', eventData.addCart);
```
-> The createAdapterBuilder function can accept an optional parameter (TrackAdapter) to handle eventdata escalation logic. By default, the ReportAdapter provided by Track is used
+## Options
-### Report data through track
+### ReportAdapter
-- Load the adapter in track
+Adapter used to process event data reporting
-- Event Data is reported through the track method provided by track
+#### `isTrackable`
-```ts
-await trackBuilder
- .before((ctx) => {
- // Execute before the track function
- })
- .after((ctx) => {
- // Execute after the track function
- })
- .transform((ctx, eventData) => {
- // Global Transform the eventData
- return eventData;
- })
- .useAdapter(() => {
- // Load all adapters
- return {
- reportData: adapter,
- };
- })
- // Filter the adapter used to process eventData
- .select(['reportData'])
- // EventType: previewGoods
- // EventData: eventData
- .track('previewGoods', eventData);
-```
+Checks if the adapter is available.
+
+#### `report`
+
+Data report
+
+### AdapterBuilder
+
+A builder for track adapter. Provides the ability to load adpater corresponding hooks
+
+#### `setup`
+
+The adapter hook Performs data consolidation against the rules defined by AdapterOptions. Passes the returned results to report. Executes before the report function is called
+
+#### `before`
+
+The adapter hook function is executed before tracking an event.
+
+#### `transform`
+
+The adapter hook function that converts EventData corresponding to different EventType
+
+#### `after`
+
+The adapter hook function is triggered after the report is executed
+
+#### `build`
+
+Return adapter instance
+
+### TrackBuilder
+
+A builder for track. Provides the ability to load track corresponding hooks
+
+#### `init`
+
+Track builder initialization, which loads the adapter into the track
+
+#### `before`
+
+A function that is executed before tracking
+
+#### `after`
-## Errors
+A function that is executed after a track event
-## Development
+#### `select`
-yarn install
+Selects track adapter from a given context, event data, and adapter map.
-## Testing
+#### `track`
-yarn test
+Event reporting activation function
From 5d8616b294cafa27de197f3bd530a9a1c7b2189a Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Mon, 12 Aug 2024 21:16:48 +0800
Subject: [PATCH 04/28] feat: update docs
---
.github/workflows/coverage-pr.yml | 34 +++
.husky/pre-commit | 3 +
package.json | 1 +
src/core/track-builder.ts | 9 +-
src/helpers/helper-adapter-track.ts | 14 +-
src/helpers/helper-select-adapter.ts | 13 +-
tests/test-adapter.spec.ts | 8 +-
tests/test-track-error.spec.ts | 10 +-
tests/test-track-execute-select.spec.ts | 147 +++++-----
tests/test-track-logger.spec.ts | 8 +-
tests/test-track-parallel-pipeline.spec.ts | 8 +-
tests/test-track-pipeline.spec.ts | 8 +-
.../adapter/default-adapter-builder.ts | 5 +-
.../adapter/default-adapter.ts | 19 +-
.../adapter/default-tack-instance.ts | 2 +-
.../adapter/report-adapter.ts | 9 +-
.../console-logger.ts | 0
.../types/type-adapter-options.ts | 0
.../types/type-event.ts | 0
.../types/type-track-data.ts | 0
vitest.config.ts | 6 +-
website/docs/adapters/google-adapter.md | 1 +
website/docs/api-reference.md | 138 ---------
website/docs/api/adapter-builder.md | 111 ++++++++
website/docs/api/base-adapter.md | 93 ++++++
website/docs/api/track-builder.md | 49 ++++
website/docs/changelog.md | 7 +
website/docs/color-validation.md | 22 --
website/docs/community/contributing.md | 5 +
website/docs/css.md | 41 ---
website/docs/faq.md | 1 +
website/docs/inheritance.md | 15 -
website/docs/intro.md | 117 +++++++-
website/docs/playground.md | 5 -
website/docs/react-hook-form.md | 48 ----
website/docs/typescript.md | 33 ---
website/sidebars.ts | 64 ++++-
.../src/components/Amimate/DynamicCoding.tsx | 34 +++
website/src/pages/index.module.css | 4 +
website/src/pages/index.tsx | 5 +
yarn.lock | 264 +++++++++++++++++-
41 files changed, 911 insertions(+), 450 deletions(-)
create mode 100644 .github/workflows/coverage-pr.yml
rename tests/{fixtures => test-utils}/adapter/default-adapter-builder.ts (86%)
rename tests/{fixtures => test-utils}/adapter/default-adapter.ts (51%)
rename tests/{fixtures => test-utils}/adapter/default-tack-instance.ts (95%)
rename tests/{fixtures => test-utils}/adapter/report-adapter.ts (76%)
rename tests/{fixtures => test-utils}/console-logger.ts (100%)
rename tests/{fixtures => test-utils}/types/type-adapter-options.ts (100%)
rename tests/{fixtures => test-utils}/types/type-event.ts (100%)
rename tests/{fixtures => test-utils}/types/type-track-data.ts (100%)
create mode 100644 website/docs/adapters/google-adapter.md
delete mode 100644 website/docs/api-reference.md
create mode 100644 website/docs/api/adapter-builder.md
create mode 100644 website/docs/api/base-adapter.md
create mode 100644 website/docs/api/track-builder.md
create mode 100644 website/docs/changelog.md
delete mode 100644 website/docs/color-validation.md
create mode 100644 website/docs/community/contributing.md
delete mode 100644 website/docs/css.md
create mode 100644 website/docs/faq.md
delete mode 100644 website/docs/inheritance.md
delete mode 100644 website/docs/playground.md
delete mode 100644 website/docs/react-hook-form.md
delete mode 100644 website/docs/typescript.md
create mode 100644 website/src/components/Amimate/DynamicCoding.tsx
diff --git a/.github/workflows/coverage-pr.yml b/.github/workflows/coverage-pr.yml
new file mode 100644
index 0000000..580e999
--- /dev/null
+++ b/.github/workflows/coverage-pr.yml
@@ -0,0 +1,34 @@
+name: "PR Build and Test"
+on:
+ pull_request:
+
+jobs:
+ build-and-test:
+ permissions:
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: "Install Node"
+ uses: actions/setup-node@v4
+ with:
+ node-version: "20.x"
+ - name: 📥 Install Dependencies
+ run: yarn --frozen-lockfile
+
+ - name: "Build"
+ run: yarn build
+
+ - name: run coverage
+ run: yarn test:coverage
+
+ # Remove node_modules to see if this action runs entirely compiled
+ - name: "Remove Node Modules"
+ run: rm -rf node_modules
+
+ - name: "PR Test Reports"
+ uses: hyperse-io/vitest-coverage-reporter@v1.0.13
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ include-all-projects: "false"
+ name: "PR Test Reports"
\ No newline at end of file
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 9eaba41..4aec334 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,5 +1,8 @@
#!/bin/sh
. "$(dirname -- "$0")/_/husky.sh"
+yarn run test:coverage
+yarn generate-coverage-report --type readme
+git add .
yarn lint-staged-files --debug
diff --git a/package.json b/package.json
index e220284..7bed498 100644
--- a/package.json
+++ b/package.json
@@ -64,6 +64,7 @@
"@commitlint/config-conventional": "19.2.2",
"@hyperse/eslint-config-hyperse": "^1.0.12",
"@hyperse/exec-program": "^1.0.10",
+ "@hyperse/vitest-coverage-reporter": "^1.0.13",
"@types/lodash": "^4",
"@types/node": "^22.1.0",
"@types/react": "^18.3.3",
diff --git a/src/core/track-builder.ts b/src/core/track-builder.ts
index 01c8915..fd46ee6 100644
--- a/src/core/track-builder.ts
+++ b/src/core/track-builder.ts
@@ -150,16 +150,9 @@ export class TrackBuilder<
async ({ trackCtx }) => {
const adapterMap = await executeSelect<
Context,
- EventType,
EventData,
AdapterMap
- >(
- trackCtx,
- eventType,
- eventData,
- innerTrackAdapterMap as AdapterMap,
- selectRule
- );
+ >(trackCtx, innerTrackAdapterMap as AdapterMap, selectRule);
return { trackCtx, adapterMap };
},
async ({ trackCtx, adapterMap }) => {
diff --git a/src/helpers/helper-adapter-track.ts b/src/helpers/helper-adapter-track.ts
index ab707f9..dd14f22 100644
--- a/src/helpers/helper-adapter-track.ts
+++ b/src/helpers/helper-adapter-track.ts
@@ -1,5 +1,6 @@
import { TrackContext } from '../types/types-create.js';
import { TrackAdapterMap, TrackEventDataBase } from '../types/types-track.js';
+import { executeFunction } from './helper-execute.js';
/**
* Executes the track function of each adapter in the adapterMap for the given eventType and result.
@@ -22,7 +23,18 @@ export const executeAdapterTrack = async <
eventType: EventType,
result: EventData[EventType]
): Promise => {
- for (const adapter of Object.values(adapterMap)) {
+ for (const [adapterName, adapter] of Object.entries(adapterMap)) {
+ const isTrackable = await executeFunction(
+ adapter.isTrackable,
+ ctx,
+ eventType,
+ result
+ );
+
+ if (!isTrackable) {
+ ctx.logger?.warn(`Adapter is not trackable: ${adapterName}`);
+ continue;
+ }
await adapter.track(ctx, eventType, result);
}
return result;
diff --git a/src/helpers/helper-select-adapter.ts b/src/helpers/helper-select-adapter.ts
index 67b6701..71ad7ec 100644
--- a/src/helpers/helper-select-adapter.ts
+++ b/src/helpers/helper-select-adapter.ts
@@ -18,13 +18,10 @@ import { isFunction } from './helper-is-function.js';
*/
export const executeSelect = async <
Context extends TrackContext,
- EventType extends keyof EventData,
EventData extends TrackEventDataBase,
TrackMap extends TrackAdapterMap,
>(
ctx: Context,
- eventType: EventType,
- eventData: EventData[EventType],
adapterMap: TrackMap,
selectRule: TrackSelectFunction = []
): Promise> => {
@@ -46,14 +43,8 @@ export const executeSelect = async <
const lasterAdapterMap: TrackAdapterMap = {};
for (const [adapterName, adapter] of Object.entries(adapterMap)) {
- const isTrackable = await executeFunction(
- adapter.isTrackable,
- ctx,
- eventType,
- eventData
- );
- if (isTrackable && names.includes(adapterName)) {
- lasterAdapterMap[adapterName] = adapterMap[adapterName];
+ if (names.includes(adapterName)) {
+ lasterAdapterMap[adapterName] = adapter;
}
}
return lasterAdapterMap;
diff --git a/tests/test-adapter.spec.ts b/tests/test-adapter.spec.ts
index b9383c8..f1622d2 100644
--- a/tests/test-adapter.spec.ts
+++ b/tests/test-adapter.spec.ts
@@ -1,9 +1,9 @@
import { createAdapterBuilder } from '../src/adapter/create-adapter-builder.js';
import { TrackContext } from '../src/types/types-create.js';
-import { ReportAdapter } from './fixtures/adapter/report-adapter.js';
-import { AdapterOptions } from './fixtures/types/type-adapter-options.js';
-import { EventDataOption } from './fixtures/types/type-event.js';
-import { TrackData } from './fixtures/types/type-track-data.js';
+import { ReportAdapter } from './test-utils/adapter/report-adapter.js';
+import { AdapterOptions } from './test-utils/types/type-adapter-options.js';
+import { EventDataOption } from './test-utils/types/type-event.js';
+import { TrackData } from './test-utils/types/type-track-data.js';
describe('test-adapter.spec', () => {
const trackData: TrackData = {
diff --git a/tests/test-track-error.spec.ts b/tests/test-track-error.spec.ts
index 792466c..be89b96 100644
--- a/tests/test-track-error.spec.ts
+++ b/tests/test-track-error.spec.ts
@@ -1,11 +1,11 @@
import { createTrackBuilder } from '../src/core/create-track-builder.js';
import { TrackAdapter } from '../src/types/types-adapter.js';
import { TrackContext } from '../src/types/types-create.js';
-import { defaultAdapterBuilder } from './fixtures/adapter/default-adapter-builder.js';
-import { ConsoleLogger } from './fixtures/console-logger.js';
-import { AdapterOptions } from './fixtures/types/type-adapter-options.js';
-import { EventDataOption } from './fixtures/types/type-event.js';
-import { TrackData } from './fixtures/types/type-track-data.js';
+import { defaultAdapterBuilder } from './test-utils/adapter/default-adapter-builder.js';
+import { ConsoleLogger } from './test-utils/console-logger.js';
+import { AdapterOptions } from './test-utils/types/type-adapter-options.js';
+import { EventDataOption } from './test-utils/types/type-event.js';
+import { TrackData } from './test-utils/types/type-track-data.js';
describe('test-track-error.spec', () => {
const trackData: TrackData = {
diff --git a/tests/test-track-execute-select.spec.ts b/tests/test-track-execute-select.spec.ts
index e92a5b6..beefe55 100644
--- a/tests/test-track-execute-select.spec.ts
+++ b/tests/test-track-execute-select.spec.ts
@@ -1,6 +1,10 @@
import { executeSelect } from '../src/helpers/helper-select-adapter.js';
-import { defaultAdapter } from './fixtures/adapter/default-adapter.js';
-import { defaultTackInstance } from './fixtures/adapter/default-tack-instance.js';
+import { createTrackBuilder } from '../src/index.js';
+import { TrackContext } from '../src/types/types-create.js';
+import { defaultAdapter } from './test-utils/adapter/default-adapter.js';
+import { ConsoleLogger } from './test-utils/console-logger.js';
+import { EventDataOption } from './test-utils/types/type-event.js';
+import { TrackData } from './test-utils/types/type-track-data.js';
describe('test-track-execute-select.spec', () => {
const ctx = {
@@ -13,6 +17,25 @@ describe('test-track-execute-select.spec', () => {
},
};
+ const eventData: EventDataOption = {
+ registry: {
+ userName: 'testUser',
+ mobile: '1234567890',
+ pwd: 'password123',
+ email: 'testuser@example.com',
+ },
+ previewGoods: {
+ goodsId: 'g123',
+ goodsName: 'Sample Goods',
+ },
+ addCart: {
+ price: 99.99,
+ goodsId: 'g123',
+ goodsName: 'Sample Goods',
+ count: 2,
+ },
+ };
+
it('executeSelect names is empty', async () => {
const adapter = defaultAdapter();
@@ -23,13 +46,7 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(
- ctx,
- 'addCart',
- {},
- adapterMap,
- undefined
- );
+ const lastAdapterMap = await executeSelect(ctx, adapterMap, undefined);
expect(Object.keys(lastAdapterMap).length).toBe(5);
expect(lastAdapterMap).toMatchObject({
@@ -53,8 +70,6 @@ describe('test-track-execute-select.spec', () => {
};
const lastAdapterMap = await executeSelect(
ctx,
- 'addCart',
- {},
adapterMap,
'consoleAdapter'
);
@@ -75,7 +90,7 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(ctx, 'addCart', {}, adapterMap, [
+ const lastAdapterMap = await executeSelect(ctx, adapterMap, [
'consoleAdapter',
'analyzerAdapter',
'reportAdapter',
@@ -100,8 +115,6 @@ describe('test-track-execute-select.spec', () => {
};
const lastAdapterMap = await executeSelect(
ctx,
- 'addCart',
- {},
adapterMap,
() => 'consoleAdapter'
);
@@ -122,13 +135,11 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(
- ctx,
- 'addCart',
- {},
- adapterMap,
- () => ['consoleAdapter', 'analyzerAdapter', 'reportAdapter']
- );
+ const lastAdapterMap = await executeSelect(ctx, adapterMap, () => [
+ 'consoleAdapter',
+ 'analyzerAdapter',
+ 'reportAdapter',
+ ]);
expect(Object.keys(lastAdapterMap).length).toBe(3);
expect(lastAdapterMap).toMatchObject({
@@ -148,12 +159,8 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(
- ctx,
- 'addCart',
- {},
- adapterMap,
- () => Promise.resolve('analyzerAdapter')
+ const lastAdapterMap = await executeSelect(ctx, adapterMap, () =>
+ Promise.resolve('analyzerAdapter')
);
expect(Object.keys(lastAdapterMap).length).toBe(1);
@@ -172,13 +179,8 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter,
businessAdapter: adapter,
};
- const lastAdapterMap = await executeSelect(
- ctx,
- 'addCart',
- {},
- adapterMap,
- () =>
- Promise.resolve(['businessAdapter', 'analyzerAdapter', 'logAdapter'])
+ const lastAdapterMap = await executeSelect(ctx, adapterMap, () =>
+ Promise.resolve(['businessAdapter', 'analyzerAdapter', 'logAdapter'])
);
expect(Object.keys(lastAdapterMap).length).toBe(3);
@@ -190,12 +192,25 @@ describe('test-track-execute-select.spec', () => {
});
it('executeSelect isTrackable', async () => {
+ const logger = new ConsoleLogger();
+ const print = vi.fn((message: any, context?: string) => {
+ return message;
+ });
+ vi.spyOn(logger, 'warn').mockImplementation(print);
+
const adapter1 = defaultAdapter();
const adapter2 = defaultAdapter();
const adapter3 = defaultAdapter();
const adapter4 = defaultAdapter();
const adapter5 = defaultAdapter();
+ const trackBuilder = createTrackBuilder<
+ TrackContext,
+ EventDataOption
+ >({
+ logger: logger,
+ });
+
vi.spyOn(adapter1, 'isTrackable').mockReturnValue(true);
vi.spyOn(adapter2, 'isTrackable').mockReturnValue(true);
vi.spyOn(adapter3, 'isTrackable').mockReturnValue(true);
@@ -209,22 +224,10 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter4,
businessAdapter: adapter5,
};
- let lastAdapterMap = await executeSelect(
- ctx,
- 'addCart',
- {},
- adapterMap,
- undefined
- );
- expect(Object.keys(lastAdapterMap).length).toBe(5);
- expect(lastAdapterMap).toMatchObject({
- consoleAdapter: adapter1,
- analyzerAdapter: adapter2,
- reportAdapter: adapter3,
- logAdapter: adapter4,
- businessAdapter: adapter5,
- });
+ await trackBuilder.init(adapterMap).track('addCart', eventData.addCart);
+ expect(print.mock.calls).toHaveLength(0);
+ expect(print.mock.results).toHaveLength(0);
vi.spyOn(adapter1, 'isTrackable').mockReturnValue(true);
vi.spyOn(adapter2, 'isTrackable').mockReturnValue(false);
@@ -239,37 +242,21 @@ describe('test-track-execute-select.spec', () => {
logAdapter: adapter4,
businessAdapter: adapter5,
};
- lastAdapterMap = await executeSelect(
- ctx,
- 'addCart',
- {},
- adapterMap,
- undefined
- );
- expect(Object.keys(lastAdapterMap).length).toBe(1);
- expect(lastAdapterMap).toMatchObject({
- consoleAdapter: adapter1,
- });
-
- vi.spyOn(adapter1, 'isTrackable').mockReturnValue(true);
- vi.spyOn(adapter2, 'isTrackable').mockReturnValue(true);
- vi.spyOn(adapter3, 'isTrackable').mockReturnValue(true);
- vi.spyOn(adapter4, 'isTrackable').mockReturnValue(false);
- vi.spyOn(adapter5, 'isTrackable').mockReturnValue(false);
+ await trackBuilder.init(adapterMap).track('addCart', eventData.addCart);
- adapterMap = {
- consoleAdapter: adapter1,
- analyzerAdapter: adapter2,
- reportAdapter: adapter3,
- logAdapter: adapter4,
- businessAdapter: adapter5,
- };
- lastAdapterMap = await executeSelect(ctx, 'addCart', {}, adapterMap, () => {
- return Promise.resolve(['reportAdapter']);
- });
- expect(Object.keys(lastAdapterMap).length).toBe(1);
- expect(lastAdapterMap).toMatchObject({
- reportAdapter: adapter3,
- });
+ expect(print.mock.calls).toHaveLength(4);
+ expect(print.mock.results).toHaveLength(4);
+ expect(print.mock.results?.[0].value).toBe(
+ 'Adapter is not trackable: analyzerAdapter'
+ );
+ expect(print.mock.results?.[1].value).toBe(
+ 'Adapter is not trackable: reportAdapter'
+ );
+ expect(print.mock.results?.[2].value).toBe(
+ 'Adapter is not trackable: logAdapter'
+ );
+ expect(print.mock.results?.[3].value).toBe(
+ 'Adapter is not trackable: businessAdapter'
+ );
});
});
diff --git a/tests/test-track-logger.spec.ts b/tests/test-track-logger.spec.ts
index f4f4eb2..9c62815 100644
--- a/tests/test-track-logger.spec.ts
+++ b/tests/test-track-logger.spec.ts
@@ -5,10 +5,10 @@ import {
TrackAdapterOptions,
TrackContext,
} from '../src/types/types-create.js';
-import { ReportAdapter } from './fixtures/adapter/report-adapter.js';
-import { ConsoleLogger } from './fixtures/console-logger.js';
-import { EventDataOption } from './fixtures/types/type-event.js';
-import { TrackData } from './fixtures/types/type-track-data.js';
+import { ReportAdapter } from './test-utils/adapter/report-adapter.js';
+import { ConsoleLogger } from './test-utils/console-logger.js';
+import { EventDataOption } from './test-utils/types/type-event.js';
+import { TrackData } from './test-utils/types/type-track-data.js';
describe('test-track-logger.spec', () => {
const trackData: TrackData = {
diff --git a/tests/test-track-parallel-pipeline.spec.ts b/tests/test-track-parallel-pipeline.spec.ts
index bd76ba1..0f83ff4 100644
--- a/tests/test-track-parallel-pipeline.spec.ts
+++ b/tests/test-track-parallel-pipeline.spec.ts
@@ -3,10 +3,10 @@ import { createAdapterBuilder } from '../src/adapter/create-adapter-builder.js';
import { createTrackBuilder } from '../src/core/create-track-builder.js';
import { TrackAdapter } from '../src/types/types-adapter.js';
import { TrackContext } from '../src/types/types-create.js';
-import { ReportAdapter } from './fixtures/adapter/report-adapter.js';
-import { AdapterOptions } from './fixtures/types/type-adapter-options.js';
-import { EventDataOption } from './fixtures/types/type-event.js';
-import { TrackData } from './fixtures/types/type-track-data.js';
+import { ReportAdapter } from './test-utils/adapter/report-adapter.js';
+import { AdapterOptions } from './test-utils/types/type-adapter-options.js';
+import { EventDataOption } from './test-utils/types/type-event.js';
+import { TrackData } from './test-utils/types/type-track-data.js';
describe('test-track-pipeline.spec', () => {
const eventData: EventDataOption = {
diff --git a/tests/test-track-pipeline.spec.ts b/tests/test-track-pipeline.spec.ts
index 9ece948..01fb715 100644
--- a/tests/test-track-pipeline.spec.ts
+++ b/tests/test-track-pipeline.spec.ts
@@ -1,10 +1,10 @@
import { createAdapterBuilder } from '../src/adapter/create-adapter-builder.js';
import { createTrackBuilder } from '../src/core/create-track-builder.js';
import { TrackContext } from '../src/types/types-create.js';
-import { ReportAdapter } from './fixtures/adapter/report-adapter.js';
-import { AdapterOptions } from './fixtures/types/type-adapter-options.js';
-import { EventDataOption } from './fixtures/types/type-event.js';
-import { TrackData } from './fixtures/types/type-track-data.js';
+import { ReportAdapter } from './test-utils/adapter/report-adapter.js';
+import { AdapterOptions } from './test-utils/types/type-adapter-options.js';
+import { EventDataOption } from './test-utils/types/type-event.js';
+import { TrackData } from './test-utils/types/type-track-data.js';
describe('test-track-pipeline.spec', () => {
const trackData: TrackData = {
diff --git a/tests/fixtures/adapter/default-adapter-builder.ts b/tests/test-utils/adapter/default-adapter-builder.ts
similarity index 86%
rename from tests/fixtures/adapter/default-adapter-builder.ts
rename to tests/test-utils/adapter/default-adapter-builder.ts
index 5fc1db9..3cf451f 100644
--- a/tests/fixtures/adapter/default-adapter-builder.ts
+++ b/tests/test-utils/adapter/default-adapter-builder.ts
@@ -7,13 +7,12 @@ import { EventDataOption } from '../types/type-event.js';
import { TrackData } from '../types/type-track-data.js';
import { ReportAdapter } from './report-adapter.js';
-export const defaultAdapterBuilder = () => {
+export const defaultAdapterBuilder = () => {
const adapter = new ReportAdapter();
- return createAdapterBuilder<
+ return createAdapterBuilder<
TrackContext,
EventDataOption,
TrackAdapterOptions, EventDataOption>
>(adapter);
-
};
diff --git a/tests/fixtures/adapter/default-adapter.ts b/tests/test-utils/adapter/default-adapter.ts
similarity index 51%
rename from tests/fixtures/adapter/default-adapter.ts
rename to tests/test-utils/adapter/default-adapter.ts
index 3a3f484..a6f03c1 100644
--- a/tests/fixtures/adapter/default-adapter.ts
+++ b/tests/test-utils/adapter/default-adapter.ts
@@ -7,13 +7,26 @@ import { EventDataOption } from '../types/type-event.js';
import { TrackData } from '../types/type-track-data.js';
import { ReportAdapter } from './report-adapter.js';
-export const defaultAdapter = () => {
+export const defaultAdapter = () => {
const adapter = new ReportAdapter();
- const adapterBuilder = createAdapterBuilder<
+ const adapterBuilder = createAdapterBuilder<
TrackContext,
EventDataOption,
TrackAdapterOptions, EventDataOption>
>(adapter);
- return adapterBuilder.build();
+ return adapterBuilder
+ .transform('addCart', (ctx, eventType, eventData) => {
+ return eventData;
+ })
+ .transform('previewGoods', (ctx, eventType, eventData) => {
+ return eventData;
+ })
+ .transform('registry', (ctx, eventType, eventData) => {
+ return eventData;
+ })
+ .transform('timeStamp', (ctx, eventType, eventData) => {
+ return eventData;
+ })
+ .build();
};
diff --git a/tests/fixtures/adapter/default-tack-instance.ts b/tests/test-utils/adapter/default-tack-instance.ts
similarity index 95%
rename from tests/fixtures/adapter/default-tack-instance.ts
rename to tests/test-utils/adapter/default-tack-instance.ts
index 76ea10f..ad8ca2a 100644
--- a/tests/fixtures/adapter/default-tack-instance.ts
+++ b/tests/test-utils/adapter/default-tack-instance.ts
@@ -4,7 +4,7 @@ import { ConsoleLogger } from '../console-logger.js';
import { TrackData } from '../types/type-track-data.js';
export const defaultTackInstance = () => {
- const configuration = {
+ const configuration = {
logger: new ConsoleLogger(),
data: {
bizMode: 'test',
diff --git a/tests/fixtures/adapter/report-adapter.ts b/tests/test-utils/adapter/report-adapter.ts
similarity index 76%
rename from tests/fixtures/adapter/report-adapter.ts
rename to tests/test-utils/adapter/report-adapter.ts
index c9bffa6..25a4887 100644
--- a/tests/fixtures/adapter/report-adapter.ts
+++ b/tests/test-utils/adapter/report-adapter.ts
@@ -10,8 +10,11 @@ export class ReportAdapter extends BaseAdapter<
EventDataOption,
AdapterOptions, EventDataOption>
> {
-
- isTrackable(ctx: TrackContext, eventType: EventType, eventData: EventDataOption[EventType]): boolean | Promise {
+ isTrackable(
+ ctx: TrackContext,
+ eventType: EventType,
+ eventData: EventDataOption[EventType]
+ ): boolean | Promise {
return true;
}
report(
@@ -19,7 +22,7 @@ export class ReportAdapter extends BaseAdapter<
reportData: AdapterReportData,
setupData?:
| {
- name: 'setup' | 'setup1' | 'setup2';
+ name: 'setup' | 'setup1' | 'setup2';
timeStamp: number;
user?: string;
}
diff --git a/tests/fixtures/console-logger.ts b/tests/test-utils/console-logger.ts
similarity index 100%
rename from tests/fixtures/console-logger.ts
rename to tests/test-utils/console-logger.ts
diff --git a/tests/fixtures/types/type-adapter-options.ts b/tests/test-utils/types/type-adapter-options.ts
similarity index 100%
rename from tests/fixtures/types/type-adapter-options.ts
rename to tests/test-utils/types/type-adapter-options.ts
diff --git a/tests/fixtures/types/type-event.ts b/tests/test-utils/types/type-event.ts
similarity index 100%
rename from tests/fixtures/types/type-event.ts
rename to tests/test-utils/types/type-event.ts
diff --git a/tests/fixtures/types/type-track-data.ts b/tests/test-utils/types/type-track-data.ts
similarity index 100%
rename from tests/fixtures/types/type-track-data.ts
rename to tests/test-utils/types/type-track-data.ts
diff --git a/vitest.config.ts b/vitest.config.ts
index 0267552..24730b6 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -10,10 +10,10 @@ export default defineConfig({
},
include: ['**/?(*.){test,spec}.?(c|m)[jt]s?(x)'],
coverage: {
- provider: 'istanbul',
- reporter: ['html'],
include: ['src/**'],
- enabled: true,
+ provider: 'istanbul',
+ reporter: ['text', 'json', 'json-summary'],
+ reportOnFailure: true,
},
},
});
diff --git a/website/docs/adapters/google-adapter.md b/website/docs/adapters/google-adapter.md
new file mode 100644
index 0000000..c538aec
--- /dev/null
+++ b/website/docs/adapters/google-adapter.md
@@ -0,0 +1 @@
+# GoogleAdapter
diff --git a/website/docs/api-reference.md b/website/docs/api-reference.md
deleted file mode 100644
index 12c9deb..0000000
--- a/website/docs/api-reference.md
+++ /dev/null
@@ -1,138 +0,0 @@
----
-sidebar_position: 3
----
-
-# API Reference
-
-This article discusses the API and props of **MuiColorInput**. Props are defined within `MuiColorInputProps`.
-
-## `value`
-
-- Type: `MuiColorInputValue`
-- Required: `true`
-
-The string parsing is very permissive. It is meant to make typing a color as input as easy as possible. All commas, percentages, parenthesis are optional, and most input allow either 0-1, 0%-100%, or 0-n (where n is either 100, 255, or 360 depending on the value).
-
-HSL and HSV both require either 0%-100% or 0-1 for the `S`/`L`/`V` properties. The `H` (hue) can have values between 0%-100% or 0-360.
-
-RGB input requires either 0-255 or 0%-100%.
-
-Source : https://github.com/scttcper/tinycolor#accepted-string-input
-
-Here are some examples of string input:
-
-### Hex, 8-digit (RGBA) Hex
-
-```tsx
-
-
-
-
-
-
-```
-
-### RGB, RGBA
-
-```tsx
-
-
-
-
-```
-
-### HSL, HSLA
-
-```tsx
-
-
-
-
-```
-
-### HSV, HSVA
-
-```tsx
-
-
-
-
-```
-
-## `onChange`
-
-- Default: `undefined`
-- Type: `(color: string, colors: MuiColorInputColors) => void`
-- Required: `false`
-
-Gets called once the user updates the color value.
-
-The callback gives you **2 parameters**:
-
-1. The new color [value](#value) stringified
-2. An object of the color value in different formats stringified (`hex`, `hex8`, `hsl`, `hsv`, `rgb`)
-
-Example:
-
-```tsx
-const handleChange = (color, colors) => {
- /**
- color: "#ffffff"
- colors: {
- hex: "#ffffff",
- hex8: "#ffffffff",
- hsl: "hsl(0, 0%, 100%)",
- hsv: "hsv(0, 0%, 100%)",
- rgb: "rgb(255, 255, 255)"
- }
- **/
-};
-
- ;
-```
-
-## `format`
-
-- Default: `"rgb"`
-- Type: `MuiColorInputFormat`
-- Required: `false`
-
-The format to use for the color [value](#value). The first parameter of `onChange` respects this format.
-
-**Available formats**: `hex`, `hex8`, `hsl`, `hsv` and `rgb`.
-
-```tsx
-
-
-
-
-
-```
-
-## `fallbackValue`
-
-- Default: `"black"`
-- Type: `MuiColorInputValue`
-- Required: `false`
-
-A fallback color [value](#value) in case the user updates the input with an invalid color value.
-
-```tsx
-
-
-
-
-
-```
-
-## `isAlphaHidden`
-
-- Default: `false`
-- Type: `boolean`
-- Required: `false`
-
-Whether to show input controls for a color’s alpha channel.
-
-```tsx
-
-```
diff --git a/website/docs/api/adapter-builder.md b/website/docs/api/adapter-builder.md
new file mode 100644
index 0000000..db1ec7f
--- /dev/null
+++ b/website/docs/api/adapter-builder.md
@@ -0,0 +1,111 @@
+# AdapterBuilder
+
+The `AdapterBuilder` class is used to create a new adapter instance. It is a factory class that creates a new adapter instance based on the provided configuration.
+
+```typescript title="Signature"
+export class AdapterBuilder<
+ Context extends TrackContext,
+ EventData extends TrackEventDataBase,
+ AdapterOptions extends TrackAdapterOptions,
+> {
+ constructor(
+ options: AdapterBuilderOptions
+ );
+}
+```
+
+## Hooks
+
+### `setup`
+
+`Function`
+
+Sets up the adapter.
+
+#### Props
+
+- **ctx** : `Context` - The track context.
+
+#### Returns
+
+- `Promise` - A promise that resolves when the adapter is set up.
+
+#### Example
+
+```typescript title="AdapterBuilder.ts"
+const adapterBuilder = new AdapterBuilder({
+ new ReportAdapter(),
+});
+
+adapterBuilder.setup(ctx);
+```
+
+### `build`
+
+`Function`
+
+Builds a new adapter instance.
+
+#### Props
+
+- **ctx** : `Context` - The track context.
+
+#### Returns
+
+- `Adapter` - A new adapter instance.
+
+#### Example
+
+```typescript title="AdapterBuilder.ts"
+const adapterBuilder = new AdapterBuilder({
+ new ReportAdapter(),
+});
+
+const adapter = adapterBuilder.build(ctx);
+```
+
+### `before`
+
+The adapter hook function is executed before tracking an event.
+
+#### Props
+
+- **event** : `EventData` - The event data.
+
+#### Returns
+
+- `EventData` - The modified event data.
+
+#### Example
+
+```typescript title="AdapterBuilder.ts"
+const adapterBuilder = new AdapterBuilder({
+ new ReportAdapter(),
+});
+
+adapterBuilder.before(event);
+```
+
+### `transform`
+
+The adapter hook function is executed to transform the event data before tracking.
+
+#### Props
+
+- **event** : `EventData` - The event data.
+
+#### Returns
+
+- `EventData` - The modified event data.
+
+#### Example
+
+```typescript title="AdapterBuilder.ts"
+const adapterBuilder = new AdapterBuilder({
+ new ReportAdapter(),
+});
+
+adapterBuilder.transform(event);
+```
+
+### `after`
diff --git a/website/docs/api/base-adapter.md b/website/docs/api/base-adapter.md
new file mode 100644
index 0000000..440b6cd
--- /dev/null
+++ b/website/docs/api/base-adapter.md
@@ -0,0 +1,93 @@
+# BaseAdapter
+
+BaseAdapter is an abstract class that serves as the base for implementing track adapters. It provides common functionality and hooks for tracking events.
+
+```typescript title="Signature"
+export interface TrackAdapter<
+ Context extends TrackContext,
+ EventData extends TrackEventDataBase,
+ AdapterOptions extends TrackAdapterOptions,
+> {
+ abstract isTrackable(
+ ctx: Context,
+ eventType: EventType,
+ eventData: EventData[EventType]
+ ): boolean | Promise;
+
+ protected report(
+ ctx: Context,
+ reportData: AdapterReportData,
+ setupData?: Required['setup'] extends (...args: any) => any
+ ? Awaited['setup']>>
+ : undefined
+ ): void | Promise {}
+}
+```
+
+### `isTrackable`
+
+`abstract Function`
+
+Checks if the adapter is available.
+
+#### Props
+
+- **ctx** : `Context` - The track context.
+- **eventType** : `EventType` - The type of the event.
+- **eventData** : `EventData[EventType]` - The data associated with the event.
+
+#### Returns
+
+- `boolean | Promise` - A boolean indicating if the adapter is available.
+
+#### Example
+
+```typescript title="ReportAdapter.ts"
+export class ReportAdapter extends BaseAdapter<
+ TrackContext,
+ EventDataOption,
+ AdapterOptions, EventDataOption>
+> {
+ isTrackable(
+ ctx: TrackContext,
+ eventType: EventType,
+ eventData: EventDataOption[EventType]
+ ): boolean | Promise {
+ return eventType === 'addCart';
+ }
+}
+```
+
+### `report`
+
+`protected Function`
+
+Reports the event to the adapter.
+
+#### Props
+
+- **ctx** : `Context` - The track context.
+- **reportData** : `AdapterReportData` - The data to report.
+- **setupData** : `Required['setup'] extends (...args: any) => any ? Awaited['setup']> : undefined` - The setup data.
+
+#### Returns
+
+- `void | Promise` - A void or a promise that resolves to void.
+
+#### Example
+
+```typescript title="ReportAdapter.ts"
+export class ReportAdapter extends BaseAdapter<
+ TrackContext,
+ EventDataOption,
+ AdapterOptions, EventDataOption>
+> {
+ report(
+ ctx: TrackContext,
+ reportData: AdapterReportData,
+ setupData?: Awaited, EventDataOption>['setup']>
+ ): void | Promise {
+ // do something
+ }
+}
+```
diff --git a/website/docs/api/track-builder.md b/website/docs/api/track-builder.md
new file mode 100644
index 0000000..ffc013b
--- /dev/null
+++ b/website/docs/api/track-builder.md
@@ -0,0 +1,49 @@
+# TrackBuilder
+
+The `TrackBuilder` class is used to create a track from a list of waypoints. The waypoints are used to create a track that can be used to generate a path for a vehicle to follow.
+
+```typescript title="Signature"
+
+```
+
+## Hooks
+
+### `before`
+
+`Function`
+
+#### Props
+
+#### Returns
+
+#### Example
+
+### `after`
+
+`Function`
+
+#### Props
+
+#### Returns
+
+#### Example
+
+### `select`
+
+`Function`
+
+#### Props
+
+#### Returns
+
+#### Example
+
+### `track`
+
+`Function`
+
+#### Props
+
+#### Returns
+
+#### Example
diff --git a/website/docs/changelog.md b/website/docs/changelog.md
new file mode 100644
index 0000000..735ea60
--- /dev/null
+++ b/website/docs/changelog.md
@@ -0,0 +1,7 @@
+---
+sidebar_position: 1
+---
+
+# ChangeLog
+
+12312
diff --git a/website/docs/color-validation.md b/website/docs/color-validation.md
deleted file mode 100644
index fc9bcb8..0000000
--- a/website/docs/color-validation.md
+++ /dev/null
@@ -1,22 +0,0 @@
----
-sidebar_position: 2
----
-
-# Color validation
-
-Maybe you need to validate the color value and check that it's correct. To do that, just import `matchIsValidColor` from the package and use this function, it will return a `boolean`.
-
-```jsx
-import React from 'react';
-import { MuiColorInput, matchIsValidColor } from 'mui-color-input';
-
-const MyComponent = () => {
- const [value, setValue] = React.useState('#ffffff');
-
- const handleChange = (newValue) => {
- matchIsValidColor(newValue); // boolean
- };
-
- return ;
-};
-```
diff --git a/website/docs/community/contributing.md b/website/docs/community/contributing.md
new file mode 100644
index 0000000..e3c5a93
--- /dev/null
+++ b/website/docs/community/contributing.md
@@ -0,0 +1,5 @@
+# contributing
+
+All contributions are welcome!
+
+Please take a moment to review guidelines [PR](https://github.com/hyperse-io/track/pulls) | [Issues](https://github.com/hyperse-io/track/issues)
diff --git a/website/docs/css.md b/website/docs/css.md
deleted file mode 100644
index baef8c2..0000000
--- a/website/docs/css.md
+++ /dev/null
@@ -1,41 +0,0 @@
----
-sidebar_position: 5
----
-
-# CSS
-
-Like any component, if you want to override a component's styles using custom classes, you can use the `className` prop.
-
-```jsx
-
-```
-
-Then, you can use the differents global class names (see below) to target an element of `MuiColorInput`.
-
-| Global class | Description |
-| ---------------------------- | ----------------------------------------------------------------------------------- |
-| `.MuiColorInput-TextField` | Styles applied to the root element. |
-| `.MuiColorInput-Button` | Styles applied to the [Button](https://mui.com/material-ui/api/button/) component |
-| `.MuiColorInput-Popover` | Styles applied to the [Popover](https://mui.com/material-ui/api/popover/) component |
-| `.MuiColorInput-ColorSpace` | Styles applied to the ColorSpace component |
-| `.MuiColorInput-HueSlider` | Styles applied to the Hue [Slider](https://mui.com/material-ui/api/slider/) |
-| `.MuiColorInput-AlphaSlider` | Styles applied to the Alpha [Slider](https://mui.com/material-ui/api/slider/) |
-
-For example: target the `.MuiColorInput-HueSlider` global class name to customize the Hue Slider.
-
-## Example with styled-component / emotion
-
-```jsx
-import { styled } from 'styled-component'; // or emotion
-import { MuiColorInput } from 'mui-color-input';
-
-const MuiColorInputStyled = styled(MuiColorInput)`
- & .MuiColorInput-AlphaSlider {
- margin-top: 10px;
- }
-`;
-
-function MyComponent() {
- return ;
-}
-```
diff --git a/website/docs/faq.md b/website/docs/faq.md
new file mode 100644
index 0000000..525e937
--- /dev/null
+++ b/website/docs/faq.md
@@ -0,0 +1 @@
+# FAQ's
diff --git a/website/docs/inheritance.md b/website/docs/inheritance.md
deleted file mode 100644
index 18bf4f6..0000000
--- a/website/docs/inheritance.md
+++ /dev/null
@@ -1,15 +0,0 @@
----
-sidebar_position: 4
----
-
-# TextField inheritance
-
-While not explicitly documented, the props of the MUI **[TextField](https://mui.com/api/text-field)** component are also available on the **MuiColorInput** component.
-
-See: https://mui.com/material-ui/api/text-field/
-
-### Example
-
-```jsx
-
-```
diff --git a/website/docs/intro.md b/website/docs/intro.md
index 0e09fab..4e1f1e2 100644
--- a/website/docs/intro.md
+++ b/website/docs/intro.md
@@ -8,13 +8,13 @@ slug: /getting-started
## Install
```bash
-npm install mui-color-input --save
+npm install @hyperse/track --save
```
or you can use **yarn**
```bash
-yarn add mui-color-input
+yarn add @hyperse/track
```
We have completed installing the package.
@@ -23,19 +23,114 @@ We have completed installing the package.
Here is a simple usage for using the component:
-```jsx
-import React from 'react';
-import { MuiColorInput } from 'mui-color-input';
-
-const MyComponent = () => {
- const [color, setColor] = React.useState('#ffffff');
+```ts
+export type Context = {
+ env: 'prod' | 'uat';
+ platform: 'android' | 'ios';
+ ip: string;
+ userId: string;
+};
- const handleChange = (color) => {
- setColor(color);
+export type EventData = {
+ registry: {
+ userName: string;
+ mobile: string;
+ pwd: string;
+ email: string;
+ };
+ addCart: {
+ price: number;
+ goodsId: string;
+ goodsName: string;
+ count: number;
};
+};
- return ;
+export type AdapterOptions = {
+ setup?: (
+ ctx: Context,
+ eventData: EventData[keyof EventData]
+ ) => Promise<{
+ name: 'setup' | 'setup1' | 'setup2';
+ timeStamp: number;
+ }>;
};
+
+// custom report adapter
+export class ReportAdapter extends BaseAdapter<
+ TrackContext,
+ EventData,
+ AdapterOptions, EventData>
+> {
+ isTrackable(
+ ctx: TrackContext,
+ eventType: EventType,
+ eventData: EventData[EventType]
+ ): boolean | Promise {
+ return true;
+ }
+ report(
+ ctx: TrackContext,
+ reportData: AdapterReportData,
+ setupData?:
+ | {
+ name: 'setup' | 'setup1' | 'setup2';
+ timeStamp: number;
+ }
+ | undefined
+ ): void | Promise {}
+}
+
+const reportAdapter = new ReportAdapter();
+
+// create adapter builder
+const adapterBuilder = createAdapterBuilder<
+ TrackContext,
+ EventData,
+ AdapterOptions, EventData>
+>(reportAdapter);
+
+// mount adapter hook
+adapterBuilder
+ .setup((ctx, eventData) => {
+ return Promise.resolve({
+ name: 'setup' as const,
+ timeStamp: new Date().getTime(),
+ newField: 'newField',
+ });
+ })
+ .before(async (ctx, eventType, eventData) => {
+ console.log('before');
+ })
+ .transform('addCart', (ctx, eventType, eventData) => {
+ return {
+ ...eventData,
+ pay: {
+ payId: 'p123',
+ payName: 'Sample Pay',
+ payType: 'credit',
+ },
+ timeStamp: '2024-09-01T00:00:00Z',
+ };
+ })
+ .after(async (ctx, eventType, eventData) => {
+ console.log('after', eventData);
+ })
+ .build();
+
+// create track builder
+const trackBuilder = createTrackBuilder<
+ TrackContext,
+ EventDataOption
+>();
+
+// mount track hook
+await trackBuilder
+ .init({ reportAdapter: reportAdapter })
+ .before(async (ctx) => {})
+ .after(async (ctx) => {})
+ .select(() => ['reportAdapter'])
+ .track('addCart', eventData.addCart);
```
## Congratulations !
diff --git a/website/docs/playground.md b/website/docs/playground.md
deleted file mode 100644
index 3bf1382..0000000
--- a/website/docs/playground.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Playground
-
-Need to play around with **MuiColorInput** in a live environment before deciding if it's the right fit? No problem.
-
-[![Open in CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/mui-color-input-fgvny5?fontsize=14&hidenavigation=1&theme=dark)
diff --git a/website/docs/react-hook-form.md b/website/docs/react-hook-form.md
deleted file mode 100644
index a4c995a..0000000
--- a/website/docs/react-hook-form.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# React Hook Form
-
-Here an example if you want to plug `MuiColorInput` to your form using [React Hook Form](https://react-hook-form.com/).
-
-```tsx
-import React from 'react';
-import ReactDOM from 'react-dom';
-import Button from '@mui/material/Button';
-import { MuiColorInput, matchIsValidColor } from 'mui-color-input';
-import { Controller, useForm } from 'react-hook-form';
-
-const App = () => {
- const { control, handleSubmit } = useForm({
- defaultValues: {
- color: '#ffffff',
- },
- });
-
- const onSubmit = (data) => {
- alert(JSON.stringify(data));
- };
-
- return (
-
- );
-};
-```
-
-[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/react-hook-form-with-mui-color-input-94iiv1?fontsize=14&hidenavigation=1&theme=dark)
diff --git a/website/docs/typescript.md b/website/docs/typescript.md
deleted file mode 100644
index 1434089..0000000
--- a/website/docs/typescript.md
+++ /dev/null
@@ -1,33 +0,0 @@
----
-sidebar_position: 4
----
-
-# TypeScript
-
-This package is written in **TypeScript**. So you don't need to create your own types. Here an example if you use **TypeScript**.
-
-**Nota bene**: Props are defined within the `MuiColorInputProps` interface.
-
-```tsx
-import React from 'react';
-import {
- MuiColorInput,
- MuiColorInputValue,
- MuiColorInputColors,
- MuiColorInputFormat,
-} from 'mui-color-input';
-
-const MyComponent = () => {
- const [value, setValue] = React.useState('#ffffff');
-
- const handleChange = (newValue: string, colors: MuiColorInputColors) => {
- setValue(newValue);
- };
-
- const format: MuiColorInputFormat = 'hex';
-
- return (
-
- );
-};
-```
diff --git a/website/sidebars.ts b/website/sidebars.ts
index 465fb41..1f5a17b 100644
--- a/website/sidebars.ts
+++ b/website/sidebars.ts
@@ -13,7 +13,64 @@ import type { SidebarsConfig } from '@docusaurus/plugin-content-docs';
const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [
- { type: 'autogenerated', dirName: '.' },
+ {
+ type: 'doc',
+ id: 'intro',
+ label: 'Getting Started',
+ },
+ {
+ type: 'category',
+ label: 'API',
+ items: [
+ {
+ type: 'doc',
+ id: 'api/base-adapter',
+ label: 'BaseAdapter',
+ },
+ {
+ type: 'doc',
+ id: 'api/adapter-builder',
+ label: 'AdapterBuilder',
+ },
+ {
+ type: 'doc',
+ id: 'api/track-builder',
+ label: 'TrackBuilder',
+ },
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Adapters',
+ items: [
+ {
+ type: 'doc',
+ id: 'adapters/google-adapter',
+ label: 'GoogleAdapter',
+ },
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Community',
+ items: [
+ {
+ type: 'doc',
+ id: 'community/contributing',
+ label: 'Contributing',
+ },
+ {
+ type: 'link',
+ label: 'ChangeLog',
+ href: 'https://github.com/hyperse-io/track/blob/main/CHANGELOG.md',
+ },
+ {
+ type: 'link',
+ label: 'Hyperse',
+ href: 'https://github.com/hyperse-io',
+ },
+ ],
+ },
{
type: 'category',
label: 'Related projects',
@@ -30,6 +87,11 @@ const sidebars: SidebarsConfig = {
},
],
},
+ {
+ type: 'doc',
+ id: 'faq',
+ label: "FAQ's",
+ },
],
};
diff --git a/website/src/components/Amimate/DynamicCoding.tsx b/website/src/components/Amimate/DynamicCoding.tsx
new file mode 100644
index 0000000..d08f40d
--- /dev/null
+++ b/website/src/components/Amimate/DynamicCoding.tsx
@@ -0,0 +1,34 @@
+import React, { useEffect, useRef } from 'react';
+
+export default function DynamicCoding(): JSX.Element {
+ const preRef = useRef(null);
+
+ const [mounted, setMounted] = React.useState(false);
+
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ useEffect(() => {
+ if (!mounted) {
+ return;
+ }
+ }, [mounted]);
+
+ return (
+
+ );
+}
diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css
index 46a46a5..98cf351 100644
--- a/website/src/pages/index.module.css
+++ b/website/src/pages/index.module.css
@@ -23,4 +23,8 @@
top: 0rem;
width: 100%;
@apply h-[15em] md:h-[30em];
+}
+
+::-webkit-scrollbar {
+ display: none;
}
\ No newline at end of file
diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx
index a7467e5..89aa50c 100644
--- a/website/src/pages/index.tsx
+++ b/website/src/pages/index.tsx
@@ -5,6 +5,7 @@ import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { CheckIcon } from '@heroicons/react/24/outline';
import { DocumentDuplicateIcon } from '@heroicons/react/24/outline';
+import DynamicCoding from '@site/src/components/Amimate/DynamicCoding';
import HomepageFeatures from '@site/src/components/HomepageFeatures';
import LogoDark from '@site/static/img/logo-dark.svg';
import LogoWhite from '@site/static/img/logo-white.svg';
@@ -105,6 +106,10 @@ export default function Home() {
>
+
diff --git a/yarn.lock b/yarn.lock
index e57c688..5bbdbef 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12,6 +12,38 @@ __metadata:
languageName: node
linkType: hard
+"@actions/core@npm:1.10.1":
+ version: 1.10.1
+ resolution: "@actions/core@npm:1.10.1"
+ dependencies:
+ "@actions/http-client": "npm:^2.0.1"
+ uuid: "npm:^8.3.2"
+ checksum: 10/d32af783ecb07f25a0f190112ee8e749a4c4cb88327d6df756ea1dcee146ab413c0e651a9c949294f8202ced1436c049d7b469485431a5cc4491d66926ec8323
+ languageName: node
+ linkType: hard
+
+"@actions/github@npm:6.0.0":
+ version: 6.0.0
+ resolution: "@actions/github@npm:6.0.0"
+ dependencies:
+ "@actions/http-client": "npm:^2.2.0"
+ "@octokit/core": "npm:^5.0.1"
+ "@octokit/plugin-paginate-rest": "npm:^9.0.0"
+ "@octokit/plugin-rest-endpoint-methods": "npm:^10.0.0"
+ checksum: 10/81831a78377175d8825fc0b94247ff366c0e87ad1dfa48df9b30b8659506f216dcf1e2d3124fcd318839b92c24ba20165e238b3cc11a34db89c69c40825e9ccf
+ languageName: node
+ linkType: hard
+
+"@actions/http-client@npm:^2.0.1, @actions/http-client@npm:^2.2.0":
+ version: 2.2.1
+ resolution: "@actions/http-client@npm:2.2.1"
+ dependencies:
+ tunnel: "npm:^0.0.6"
+ undici: "npm:^5.25.4"
+ checksum: 10/b7338f13461eeca945acc9ccdd20a46e545624dc872bc12869eca7d9a58536ee3e1ecc0d1a1d4c16e8610c2b783e7108e12148b6db5d7fb8bf8b950b8a002d66
+ languageName: node
+ linkType: hard
+
"@algolia/autocomplete-core@npm:1.9.3":
version: 1.9.3
resolution: "@algolia/autocomplete-core@npm:1.9.3"
@@ -3114,6 +3146,13 @@ __metadata:
languageName: node
linkType: hard
+"@fastify/busboy@npm:^2.0.0":
+ version: 2.1.1
+ resolution: "@fastify/busboy@npm:2.1.1"
+ checksum: 10/2bb8a7eca8289ed14c9eb15239bc1019797454624e769b39a0b90ed204d032403adc0f8ed0d2aef8a18c772205fa7808cf5a1b91f21c7bfc7b6032150b1062c5
+ languageName: node
+ linkType: hard
+
"@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0":
version: 9.3.0
resolution: "@hapi/hoek@npm:9.3.0"
@@ -3251,6 +3290,7 @@ __metadata:
"@hyperse/eslint-config-hyperse": "npm:^1.0.12"
"@hyperse/exec-program": "npm:^1.0.10"
"@hyperse/pipeline": "npm:^1.0.4"
+ "@hyperse/vitest-coverage-reporter": "npm:^1.0.13"
"@types/lodash": "npm:^4"
"@types/node": "npm:^22.1.0"
"@types/react": "npm:^18.3.3"
@@ -3286,6 +3326,25 @@ __metadata:
languageName: node
linkType: hard
+"@hyperse/vitest-coverage-reporter@npm:^1.0.13":
+ version: 1.0.13
+ resolution: "@hyperse/vitest-coverage-reporter@npm:1.0.13"
+ dependencies:
+ "@actions/core": "npm:1.10.1"
+ "@actions/github": "npm:6.0.0"
+ "@manypkg/get-packages": "npm:^2.2.2"
+ common-tags: "npm:1.8.2"
+ mdast-util-to-string: "npm:^4.0.0"
+ minimist: "npm:^1.2.8"
+ remark-parse: "npm:^11.0.0"
+ remark-stringify: "npm:^11.0.0"
+ unified: "npm:^11.0.5"
+ bin:
+ generate-coverage-report: bin/generate-coverage-report.mjs
+ checksum: 10/d3e506e97d8988df27ea8f16005970b407a6034fe47e7b320618951eb60df2c1b0b2acfc29370edd509d988a43df010ad2e2f52cda042886e0256b555aea9fca
+ languageName: node
+ linkType: hard
+
"@isaacs/cliui@npm:^8.0.2":
version: 8.0.2
resolution: "@isaacs/cliui@npm:8.0.2"
@@ -3411,6 +3470,15 @@ __metadata:
languageName: node
linkType: hard
+"@manypkg/find-root@npm:^2.2.2":
+ version: 2.2.3
+ resolution: "@manypkg/find-root@npm:2.2.3"
+ dependencies:
+ "@manypkg/tools": "npm:^1.1.2"
+ checksum: 10/b0e6e530bda0019ea098fc88903c703b64f0eee3e568eb7e82258b1b487c330c76648bd27569835fcc61cf60d15d257a2779ac0051d1bc8a682f0fb629b989ef
+ languageName: node
+ linkType: hard
+
"@manypkg/get-packages@npm:^1.1.3":
version: 1.1.3
resolution: "@manypkg/get-packages@npm:1.1.3"
@@ -3425,6 +3493,27 @@ __metadata:
languageName: node
linkType: hard
+"@manypkg/get-packages@npm:^2.2.2":
+ version: 2.2.2
+ resolution: "@manypkg/get-packages@npm:2.2.2"
+ dependencies:
+ "@manypkg/find-root": "npm:^2.2.2"
+ "@manypkg/tools": "npm:^1.1.1"
+ checksum: 10/90a6f08a0b79f8edefeddb34eb0f1dad9ae08f5a86fc0df02349111cb85a239feb5aefb20d82c097caff2181581b3ec59b043c21cafd32e6baf9af6b19cc9de4
+ languageName: node
+ linkType: hard
+
+"@manypkg/tools@npm:^1.1.1, @manypkg/tools@npm:^1.1.2":
+ version: 1.1.2
+ resolution: "@manypkg/tools@npm:1.1.2"
+ dependencies:
+ fast-glob: "npm:^3.3.2"
+ jju: "npm:^1.4.0"
+ js-yaml: "npm:^4.1.0"
+ checksum: 10/f3d57b52d07634aef92a136e8f02009626ce7cc283d77d129787bf81d1ad8b0dae9a2cedfd794bd71caf601184929125fc2f4aa105bc0a8d3fcf56a0ce1cf0a0
+ languageName: node
+ linkType: hard
+
"@mdx-js/mdx@npm:^3.0.0":
version: 3.0.1
resolution: "@mdx-js/mdx@npm:3.0.1"
@@ -3631,6 +3720,126 @@ __metadata:
languageName: node
linkType: hard
+"@octokit/auth-token@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "@octokit/auth-token@npm:4.0.0"
+ checksum: 10/60e42701e341d700f73c518c7a35675d36d79fa9d5e838cc3ade96d147e49f5ba74db2e07b2337c2b95aaa540aa42088116df2122daa25633f9e70a2c8785c44
+ languageName: node
+ linkType: hard
+
+"@octokit/core@npm:^5.0.1":
+ version: 5.2.0
+ resolution: "@octokit/core@npm:5.2.0"
+ dependencies:
+ "@octokit/auth-token": "npm:^4.0.0"
+ "@octokit/graphql": "npm:^7.1.0"
+ "@octokit/request": "npm:^8.3.1"
+ "@octokit/request-error": "npm:^5.1.0"
+ "@octokit/types": "npm:^13.0.0"
+ before-after-hook: "npm:^2.2.0"
+ universal-user-agent: "npm:^6.0.0"
+ checksum: 10/2e40baf0b5c6949922436a653c213be43befd9690c43dd89872f669f3ac23117ae8ae5e5d6c18094813756c71c3f4fbedd575a891f0b89e12f58b2c38b7f3c13
+ languageName: node
+ linkType: hard
+
+"@octokit/endpoint@npm:^9.0.1":
+ version: 9.0.5
+ resolution: "@octokit/endpoint@npm:9.0.5"
+ dependencies:
+ "@octokit/types": "npm:^13.1.0"
+ universal-user-agent: "npm:^6.0.0"
+ checksum: 10/212122f653bf076ec37dd7de44bd54db74aa3cd16be4c395c91444488331becd83351e26b30248168e2cc28fc07b1a96e8f74adbbab02826f76de92e069f391f
+ languageName: node
+ linkType: hard
+
+"@octokit/graphql@npm:^7.1.0":
+ version: 7.1.0
+ resolution: "@octokit/graphql@npm:7.1.0"
+ dependencies:
+ "@octokit/request": "npm:^8.3.0"
+ "@octokit/types": "npm:^13.0.0"
+ universal-user-agent: "npm:^6.0.0"
+ checksum: 10/da6857a69dc93cd20a11d3a905db4214d269d246a6aaee1d8734f922024b08ffdef0b3cba2ac79917633043b4f50464242b0bd92a265c960083dfff5b833dbbe
+ languageName: node
+ linkType: hard
+
+"@octokit/openapi-types@npm:^20.0.0":
+ version: 20.0.0
+ resolution: "@octokit/openapi-types@npm:20.0.0"
+ checksum: 10/9f60572af1201dd92626c412253d83d986b8ab1956250b95f417013ee8e7baf25870eeb801d16672cabc2c420544bc9c2f0a979e07603ff5997eff038c71a8c3
+ languageName: node
+ linkType: hard
+
+"@octokit/openapi-types@npm:^22.2.0":
+ version: 22.2.0
+ resolution: "@octokit/openapi-types@npm:22.2.0"
+ checksum: 10/0471b0c789fada5aa2390e6f82ba477738228ef7d2d986dda9aab0cb625d1562bd178ba0ba4d2655ce841079cd5efff9e58ece2077c27e569ea22109ea301830
+ languageName: node
+ linkType: hard
+
+"@octokit/plugin-paginate-rest@npm:^9.0.0":
+ version: 9.2.1
+ resolution: "@octokit/plugin-paginate-rest@npm:9.2.1"
+ dependencies:
+ "@octokit/types": "npm:^12.6.0"
+ peerDependencies:
+ "@octokit/core": 5
+ checksum: 10/1528ab17eedb6705e30ad8576493f06b40f29a87c920a4affeb9715fe5f386e064b79eadd401c0cd1e7ec22287a461da4f5353a4ee57bc614fd890b0aa139d77
+ languageName: node
+ linkType: hard
+
+"@octokit/plugin-rest-endpoint-methods@npm:^10.0.0":
+ version: 10.4.1
+ resolution: "@octokit/plugin-rest-endpoint-methods@npm:10.4.1"
+ dependencies:
+ "@octokit/types": "npm:^12.6.0"
+ peerDependencies:
+ "@octokit/core": 5
+ checksum: 10/1090fc5a1bebb7b48c512e178f8ad69a3ef8332e583274972f3a3035e9be9200093e22a5dbfe0f71aa1a7a8817e54bb915af3c2a3f88db1311a2873cef176552
+ languageName: node
+ linkType: hard
+
+"@octokit/request-error@npm:^5.1.0":
+ version: 5.1.0
+ resolution: "@octokit/request-error@npm:5.1.0"
+ dependencies:
+ "@octokit/types": "npm:^13.1.0"
+ deprecation: "npm:^2.0.0"
+ once: "npm:^1.4.0"
+ checksum: 10/d03f9f7a408af673cd991eeb450b6f4a5cee6c368f6349eb0211dfc0404fddfcff8b5225ef186020a2a1829adba0aa8c9174155b49ab2ed00a94fb9a886a1dd3
+ languageName: node
+ linkType: hard
+
+"@octokit/request@npm:^8.3.0, @octokit/request@npm:^8.3.1":
+ version: 8.4.0
+ resolution: "@octokit/request@npm:8.4.0"
+ dependencies:
+ "@octokit/endpoint": "npm:^9.0.1"
+ "@octokit/request-error": "npm:^5.1.0"
+ "@octokit/types": "npm:^13.1.0"
+ universal-user-agent: "npm:^6.0.0"
+ checksum: 10/176cd83c68bde87111a01d50e2d21cf12ec362c1a30b33649eb8771d37397f6d6dd0b0844aab8d59b16d74c825252e39cadd52e37a4b1669d6facd1cb2cdc995
+ languageName: node
+ linkType: hard
+
+"@octokit/types@npm:^12.6.0":
+ version: 12.6.0
+ resolution: "@octokit/types@npm:12.6.0"
+ dependencies:
+ "@octokit/openapi-types": "npm:^20.0.0"
+ checksum: 10/19b77a8d25af2a5df4561f8750f807edfc9fca5b07cfa9fb21dce4665e1b188c966688f5ed5e08089404428100dfe44ad353f8d8532f1d30fe47e61c5faa1440
+ languageName: node
+ linkType: hard
+
+"@octokit/types@npm:^13.0.0, @octokit/types@npm:^13.1.0":
+ version: 13.5.0
+ resolution: "@octokit/types@npm:13.5.0"
+ dependencies:
+ "@octokit/openapi-types": "npm:^22.2.0"
+ checksum: 10/d2aeebc1d8684c4e950f054a52b484e898b72d9f5f8433bcf010161716eea20d1132820d922212f19557a8f147354f2674d1a27b22941308b7c298bdd2674ffa
+ languageName: node
+ linkType: hard
+
"@pkgjs/parseargs@npm:^0.11.0":
version: 0.11.0
resolution: "@pkgjs/parseargs@npm:0.11.0"
@@ -5606,6 +5815,13 @@ __metadata:
languageName: node
linkType: hard
+"before-after-hook@npm:^2.2.0":
+ version: 2.2.3
+ resolution: "before-after-hook@npm:2.2.3"
+ checksum: 10/e676f769dbc4abcf4b3317db2fd2badb4a92c0710e0a7da12cf14b59c3482d4febf835ad7de7874499060fd4e13adf0191628e504728b3c5bb4ec7a878c09940
+ languageName: node
+ linkType: hard
+
"better-path-resolve@npm:1.0.0":
version: 1.0.0
resolution: "better-path-resolve@npm:1.0.0"
@@ -6399,6 +6615,13 @@ __metadata:
languageName: node
linkType: hard
+"common-tags@npm:1.8.2":
+ version: 1.8.2
+ resolution: "common-tags@npm:1.8.2"
+ checksum: 10/c665d0f463ee79dda801471ad8da6cb33ff7332ba45609916a508ad3d77ba07ca9deeb452e83f81f24c2b081e2c1315347f23d239210e63d1c5e1a0c7c019fe2
+ languageName: node
+ linkType: hard
+
"compare-func@npm:^2.0.0":
version: 2.0.0
resolution: "compare-func@npm:2.0.0"
@@ -7184,6 +7407,13 @@ __metadata:
languageName: node
linkType: hard
+"deprecation@npm:^2.0.0":
+ version: 2.3.1
+ resolution: "deprecation@npm:2.3.1"
+ checksum: 10/f56a05e182c2c195071385455956b0c4106fe14e36245b00c689ceef8e8ab639235176a96977ba7c74afb173317fac2e0ec6ec7a1c6d1e6eaa401c586c714132
+ languageName: node
+ linkType: hard
+
"dequal@npm:^2.0.0":
version: 2.0.3
resolution: "dequal@npm:2.0.3"
@@ -10910,6 +11140,13 @@ __metadata:
languageName: node
linkType: hard
+"jju@npm:^1.4.0":
+ version: 1.4.0
+ resolution: "jju@npm:1.4.0"
+ checksum: 10/1067ff8ce02221faac5a842116ed0ec79a53312a111d0bf8342a80bd02c0a3fdf0b8449694a65947db0a3e8420e8b326dffb489c7dd5866efc380c0d1708a707
+ languageName: node
+ linkType: hard
+
"joi@npm:^17.9.2":
version: 17.13.3
resolution: "joi@npm:17.13.3"
@@ -13133,7 +13370,7 @@ __metadata:
languageName: node
linkType: hard
-"once@npm:^1.3.0":
+"once@npm:^1.3.0, once@npm:^1.4.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
@@ -16873,6 +17110,13 @@ __metadata:
languageName: node
linkType: hard
+"tunnel@npm:^0.0.6":
+ version: 0.0.6
+ resolution: "tunnel@npm:0.0.6"
+ checksum: 10/cf1ffed5e67159b901a924dbf94c989f20b2b3b65649cfbbe4b6abb35955ce2cf7433b23498bdb2c5530ab185b82190fce531597b3b4a649f06a907fc8702405
+ languageName: node
+ linkType: hard
+
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
version: 0.4.0
resolution: "type-check@npm:0.4.0"
@@ -17048,6 +17292,15 @@ __metadata:
languageName: node
linkType: hard
+"undici@npm:^5.25.4":
+ version: 5.28.4
+ resolution: "undici@npm:5.28.4"
+ dependencies:
+ "@fastify/busboy": "npm:^2.0.0"
+ checksum: 10/a666a9f5ac4270c659fafc33d78b6b5039a0adbae3e28f934774c85dcc66ea91da907896f12b414bd6f578508b44d5dc206fa636afa0e49a4e1c9e99831ff065
+ languageName: node
+ linkType: hard
+
"unicode-canonical-property-names-ecmascript@npm:^2.0.0":
version: 2.0.0
resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0"
@@ -17122,7 +17375,7 @@ __metadata:
languageName: node
linkType: hard
-"unified@npm:^11.0.0, unified@npm:^11.0.3, unified@npm:^11.0.4":
+"unified@npm:^11.0.0, unified@npm:^11.0.3, unified@npm:^11.0.4, unified@npm:^11.0.5":
version: 11.0.5
resolution: "unified@npm:11.0.5"
dependencies:
@@ -17249,6 +17502,13 @@ __metadata:
languageName: node
linkType: hard
+"universal-user-agent@npm:^6.0.0":
+ version: 6.0.1
+ resolution: "universal-user-agent@npm:6.0.1"
+ checksum: 10/fdc8e1ae48a05decfc7ded09b62071f571c7fe0bd793d700704c80cea316101d4eac15cc27ed2bb64f4ce166d2684777c3198b9ab16034f547abea0d3aa1c93c
+ languageName: node
+ linkType: hard
+
"universalify@npm:^0.1.0":
version: 0.1.2
resolution: "universalify@npm:0.1.2"
From 785218a5ef17ef4a3fee0ef2abf3450f4ccf2f7a Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Mon, 12 Aug 2024 21:22:57 +0800
Subject: [PATCH 05/28] feat: add vitest reporter
---
.husky/pre-commit | 1 -
README.md | 6 ++++++
package.json | 1 +
vitest.config.ts | 2 +-
4 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 4aec334..95dc3ee 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,5 +1,4 @@
#!/bin/sh
-. "$(dirname -- "$0")/_/husky.sh"
yarn run test:coverage
yarn generate-coverage-report --type readme
diff --git a/README.md b/README.md
index ca509a4..84df188 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,12 @@
+
+
+## Coverage Report
+
+ Status Category Percentage Covered / Total 🔵 Lines 100% 185 / 185 🔵 Statements 100% 185 / 185 🔵 Functions 98.18% 54 / 55 🔵 Branches 93.75% 75 / 80
+
A typed, smart, scalable , powerful data collection engine written in typescript
## Install
diff --git a/package.json b/package.json
index 7bed498..02f0485 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"lint-staged-files": "lint-staged --allow-empty",
"changeset": "changeset",
"release": "yarn build && changeset publish",
+ "test:coverage": "vitest run --coverage",
"cz": "cz",
"prepare": "husky"
},
diff --git a/vitest.config.ts b/vitest.config.ts
index 24730b6..e0123dd 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -12,7 +12,7 @@ export default defineConfig({
coverage: {
include: ['src/**'],
provider: 'istanbul',
- reporter: ['text', 'json', 'json-summary'],
+ reporter: ['text', 'json-summary', 'json'],
reportOnFailure: true,
},
},
From 81494dd32913d8e055b08767f0521d1e9e96bddf Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Tue, 13 Aug 2024 20:54:36 +0800
Subject: [PATCH 06/28] feat: perfect docs
---
src/adapter/adapter-base.ts | 9 +++++----
src/types/types-create.ts | 6 ++++--
tests/test-adapter.spec.ts | 5 +++--
tests/test-utils/types/type-adapter-options.ts | 5 +++--
website/docs/api/adapter-builder.md | 12 +++++++++---
website/docs/api/base-adapter.md | 2 +-
6 files changed, 25 insertions(+), 14 deletions(-)
diff --git a/src/adapter/adapter-base.ts b/src/adapter/adapter-base.ts
index 4d2bfc7..9d28dd1 100644
--- a/src/adapter/adapter-base.ts
+++ b/src/adapter/adapter-base.ts
@@ -82,14 +82,15 @@ export abstract class BaseAdapter<
return result;
};
- private executeReport = async (
+ private executeReport = async (
ctx: Context,
- eventData: EventData[keyof EventData],
+ eventType: EventType,
+ eventData: EventData[EventType],
reportData: ReportData
): Promise => {
let setupResult;
if (this.setupHook) {
- setupResult = await this.setupHook(ctx, eventData);
+ setupResult = await this.setupHook(ctx, eventType, eventData);
}
await this.report(ctx, reportData, setupResult);
return reportData;
@@ -113,7 +114,7 @@ export abstract class BaseAdapter<
await executeFunction(this.beforeHook, ctx, eventType, eventData),
async () => await this.executeTransform(ctx, eventType, eventData),
async (reportData) =>
- await this.executeReport(ctx, eventData, reportData),
+ await this.executeReport(ctx, eventType, eventData, reportData),
async (reportData) =>
await executeFunction(this.afterHook, ctx, eventType, reportData)
)();
diff --git a/src/types/types-create.ts b/src/types/types-create.ts
index 7003632..cea9360 100644
--- a/src/types/types-create.ts
+++ b/src/types/types-create.ts
@@ -55,11 +55,13 @@ export type TrackAdapterOptions<
* Executes before the report function is called
*
* @param ctx - The track context.
+ * @param eventType - The event type.
* @param eventData - The event data.
* @returns A value or a promise that resolves to a value.
*/
- setup?: (
+ setup?: (
ctx: Context,
- eventData: EventData[keyof EventData]
+ eventType: EventType,
+ eventData: EventData[EventType]
) => any | Promise;
};
diff --git a/tests/test-adapter.spec.ts b/tests/test-adapter.spec.ts
index f1622d2..8ef2d67 100644
--- a/tests/test-adapter.spec.ts
+++ b/tests/test-adapter.spec.ts
@@ -52,7 +52,7 @@ describe('test-adapter.spec', () => {
AdapterOptions, EventDataOption>
>(reportAdapter);
- const setupFun = vi.fn((ctx, eventData) => {
+ const setupFun = vi.fn((ctx, eventType, eventData) => {
return Promise.resolve({
name: 'setup' as const,
timeStamp: new Date().getTime(),
@@ -89,7 +89,8 @@ describe('test-adapter.spec', () => {
expect(setupFun.mock.lastCall?.[0]).toMatchObject({
data: trackData,
});
- expect(setupFun.mock.lastCall?.[1]).toMatchObject({ ...eventData.addCart });
+ expect(setupFun.mock.lastCall?.[1]).toBe('addCart');
+ expect(setupFun.mock.lastCall?.[2]).toMatchObject({ ...eventData.addCart });
expect(setupFun.mock.results[0].value).toBeDefined();
expect(setupFun.mock.results?.[0].value).toMatchObject(
Promise.resolve({
diff --git a/tests/test-utils/types/type-adapter-options.ts b/tests/test-utils/types/type-adapter-options.ts
index fcb2633..d03cc73 100644
--- a/tests/test-utils/types/type-adapter-options.ts
+++ b/tests/test-utils/types/type-adapter-options.ts
@@ -1,7 +1,8 @@
export type AdapterOptions = {
- setup?: (
+ setup?: (
ctx: Context,
- eventData: EventData[keyof EventData]
+ eventTYpe: EventType,
+ eventData: EventData[EventType]
) => Promise<{
name: 'setup' | 'setup1' | 'setup2';
timeStamp: number;
diff --git a/website/docs/api/adapter-builder.md b/website/docs/api/adapter-builder.md
index db1ec7f..d018a53 100644
--- a/website/docs/api/adapter-builder.md
+++ b/website/docs/api/adapter-builder.md
@@ -18,17 +18,23 @@ export class AdapterBuilder<
### `setup`
-`Function`
+`(
+ ctx: Context,
+ eventType: EventType,
+ eventData: EventData[EventType]
+ ) => any | Promise`
-Sets up the adapter.
+The setup data. It is often useful to extend the report method by configuring some additional data to be used in the report phase without the transform processing
#### Props
- **ctx** : `Context` - The track context.
+- **eventType** : `EventType` - The event type.
+- **eventData** : `EventData[EventType]` - The event data.
#### Returns
-- `Promise` - A promise that resolves when the adapter is set up.
+- `any` | `Promise` - A value or a promise that resolves to a value.
#### Example
diff --git a/website/docs/api/base-adapter.md b/website/docs/api/base-adapter.md
index 440b6cd..4b4bf80 100644
--- a/website/docs/api/base-adapter.md
+++ b/website/docs/api/base-adapter.md
@@ -68,7 +68,7 @@ Reports the event to the adapter.
- **ctx** : `Context` - The track context.
- **reportData** : `AdapterReportData` - The data to report.
-- **setupData** : `Required['setup'] extends (...args: any) => any ? Awaited['setup']> : undefined` - The setup data.
+- **setupData** : `Required['setup'] extends (...args: any) => any ? Awaited['setup']> : undefined` - The setup data. It is often useful to extend the report method by configuring some additional data to be used in the report phase without the transform processing
#### Returns
From 5030255f43e144fdad215f82641fdbca893aedb9 Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Tue, 13 Aug 2024 22:03:54 +0800
Subject: [PATCH 07/28] docs: perfect docs
---
README.md | 28 +++---
hyperse-track.code-workspace | 3 +
tests/test-track-docs.spec.ts | 141 +++++++++++++++++++++++++++
website/docs/api/adapter-builder.md | 144 +++++++++++++++++-----------
website/docs/api/track-builder.md | 93 +++++++++++++++---
website/src/pages/index.tsx | 5 -
6 files changed, 325 insertions(+), 89 deletions(-)
create mode 100644 tests/test-track-docs.spec.ts
diff --git a/README.md b/README.md
index 84df188..a0be893 100644
--- a/README.md
+++ b/README.md
@@ -147,15 +147,11 @@ await trackBuilder
## Options
-### ReportAdapter
-
-Adapter used to process event data reporting
-
-#### `isTrackable`
+#### [`isTrackable`](https://hyperse-io.github.io/track/docs/api/base-adapter#istrackable)
Checks if the adapter is available.
-#### `report`
+#### [`report`](https://hyperse-io.github.io/track/docs/api/base-adapter#istrackable)
Data report
@@ -163,23 +159,23 @@ Data report
A builder for track adapter. Provides the ability to load adpater corresponding hooks
-#### `setup`
+#### [`setup`](https://hyperse-io.github.io/track/docs/api/adapter-builder#setup)
The adapter hook Performs data consolidation against the rules defined by AdapterOptions. Passes the returned results to report. Executes before the report function is called
-#### `before`
+#### [`before`](https://hyperse-io.github.io/track/docs/api/adapter-builder#before)
The adapter hook function is executed before tracking an event.
-#### `transform`
+#### [`transform`](https://hyperse-io.github.io/track/docs/api/adapter-builder#transform)
The adapter hook function that converts EventData corresponding to different EventType
-#### `after`
+#### [`after`](https://hyperse-io.github.io/track/docs/api/adapter-builder#after)
The adapter hook function is triggered after the report is executed
-#### `build`
+#### [`build`](https://hyperse-io.github.io/track/docs/api/adapter-builder#build)
Return adapter instance
@@ -187,22 +183,22 @@ Return adapter instance
A builder for track. Provides the ability to load track corresponding hooks
-#### `init`
+#### [`init`](https://hyperse-io.github.io/track/docs/api/track-builder#init)
Track builder initialization, which loads the adapter into the track
-#### `before`
+#### [`before`](https://hyperse-io.github.io/track/docs/api/track-builder#before)
A function that is executed before tracking
-#### `after`
+#### [`after`](https://hyperse-io.github.io/track/docs/api/track-builder#after)
A function that is executed after a track event
-#### `select`
+#### [`select`](https://hyperse-io.github.io/track/docs/api/track-builder#select)
Selects track adapter from a given context, event data, and adapter map.
-#### `track`
+#### [`track`](https://hyperse-io.github.io/track/docs/api/track-builder#track)
Event reporting activation function
diff --git a/hyperse-track.code-workspace b/hyperse-track.code-workspace
index 19d0497..e0751a9 100644
--- a/hyperse-track.code-workspace
+++ b/hyperse-track.code-workspace
@@ -56,5 +56,8 @@
"i18n-ally.localesPaths": [
"i18n"
],
+ "[markdown]": {
+ "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+ },
},
}
\ No newline at end of file
diff --git a/tests/test-track-docs.spec.ts b/tests/test-track-docs.spec.ts
new file mode 100644
index 0000000..02370da
--- /dev/null
+++ b/tests/test-track-docs.spec.ts
@@ -0,0 +1,141 @@
+import { createAdapterBuilder } from '../src/adapter/create-adapter-builder.js';
+import { createTrackBuilder } from '../src/core/create-track-builder.js';
+import { TrackContext } from '../src/types/types-create.js';
+import { ReportAdapter } from './test-utils/adapter/report-adapter.js';
+import { AdapterOptions } from './test-utils/types/type-adapter-options.js';
+import { EventDataOption } from './test-utils/types/type-event.js';
+import { TrackData } from './test-utils/types/type-track-data.js';
+
+describe('test-track-pipeline.spec', () => {
+ const trackData: TrackData = {
+ bizMode: 'test',
+ env: 'prod',
+ platform: 'android',
+ ip: '0.0.0.0',
+ userId: 'uuid_10001',
+ };
+
+ const eventData: EventDataOption = {
+ registry: {
+ userName: 'testUser',
+ mobile: '1234567890',
+ pwd: 'password123',
+ email: 'testuser@example.com',
+ },
+ previewGoods: {
+ goodsId: 'g123',
+ goodsName: 'Sample Goods',
+ },
+ addCart: {
+ price: 99.99,
+ goodsId: 'g123',
+ goodsName: 'Sample Goods',
+ count: 2,
+ },
+ timeStamp: '1000',
+ };
+
+ it('docs example', async () => {
+ const adapter = new ReportAdapter();
+
+ const adapterBuilder = createAdapterBuilder<
+ TrackContext,
+ EventDataOption,
+ AdapterOptions, EventDataOption>
+ >(adapter);
+
+ adapterBuilder
+ .setup(
+ (
+ ctx: TrackContext,
+ eventType: keyof EventDataOption,
+ eventData: EventDataOption[keyof EventDataOption]
+ ) => {
+ return Promise.resolve({
+ name: 'setup',
+ timeStamp: Date.now(),
+ });
+ }
+ )
+ .before(
+ (
+ ctx: TrackContext,
+ eventType: keyof EventDataOption,
+ eventData: EventDataOption[keyof EventDataOption]
+ ) => {
+ //do something
+ }
+ )
+ .transform(
+ 'addCart',
+ (
+ ctx: TrackContext,
+ eventType: 'addCart',
+ eventData: EventDataOption['addCart']
+ ) => {
+ return {
+ ...eventData,
+ goodName: 'ac_' + eventData?.goodsName,
+ timeStamp: Date.now(),
+ };
+ }
+ )
+ .transform(
+ 'previewGoods',
+ (
+ ctx: TrackContext,
+ eventType: 'previewGoods',
+ eventData: EventDataOption['previewGoods']
+ ) => {
+ return {
+ ...eventData,
+ goodName: 'pg_' + eventData?.goodsName,
+ timeStamp: Date.now(),
+ };
+ }
+ )
+ .after(
+ (
+ ctx: TrackContext,
+ eventType: keyof EventDataOption,
+ eventData: EventDataOption[keyof EventDataOption]
+ ) => {
+ //do something
+ }
+ )
+ .build();
+
+ const trackBuilder = createTrackBuilder<
+ TrackContext,
+ EventDataOption
+ >();
+
+ await trackBuilder
+ .init(() => {
+ return { reportAdapter: adapter, consoleAdapter: adapter };
+ })
+ .before(async (ctx) => {
+ // do something
+ })
+ .after(async (ctx) => {
+ // do something
+ })
+ .select(
+ (
+ ctx: TrackContext,
+ adapterMap: {
+ reportAdapter: ReportAdapter;
+ consoleAdapter: ReportAdapter;
+ }
+ ) => 'consoleAdapter'
+ )
+ .track('addCart', {
+ price: 99.99,
+ goodsId: 'g123',
+ goodsName: 'Sample Goods',
+ count: 2,
+ });
+
+ expect(true).toBeTruthy();
+ });
+});
diff --git a/website/docs/api/adapter-builder.md b/website/docs/api/adapter-builder.md
index d018a53..c761589 100644
--- a/website/docs/api/adapter-builder.md
+++ b/website/docs/api/adapter-builder.md
@@ -3,14 +3,12 @@
The `AdapterBuilder` class is used to create a new adapter instance. It is a factory class that creates a new adapter instance based on the provided configuration.
```typescript title="Signature"
-export class AdapterBuilder<
+class AdapterBuilder<
Context extends TrackContext,
EventData extends TrackEventDataBase,
AdapterOptions extends TrackAdapterOptions,
> {
- constructor(
- options: AdapterBuilderOptions
- );
+ constructor(adapter: TrackAdapter);
}
```
@@ -18,12 +16,6 @@ export class AdapterBuilder<
### `setup`
-`(
- ctx: Context,
- eventType: EventType,
- eventData: EventData[EventType]
- ) => any | Promise`
-
The setup data. It is often useful to extend the report method by configuring some additional data to be used in the report phase without the transform processing
#### Props
@@ -32,86 +24,128 @@ The setup data. It is often useful to extend the report method by configuring so
- **eventType** : `EventType` - The event type.
- **eventData** : `EventData[EventType]` - The event data.
-#### Returns
-
-- `any` | `Promise` - A value or a promise that resolves to a value.
-
#### Example
```typescript title="AdapterBuilder.ts"
-const adapterBuilder = new AdapterBuilder({
- new ReportAdapter(),
-});
-
-adapterBuilder.setup(ctx);
+adapterBuilder.setup(
+ (
+ ctx: TrackContext,
+ eventType: keyof EventDataOption,
+ eventData: EventDataOption[keyof EventDataOption]
+ ) => {
+ return Promise.resolve({
+ name: 'setup',
+ timeStamp: Date.now(),
+ });
+ }
+);
```
-### `build`
-
-`Function`
+### `before`
-Builds a new adapter instance.
+The adapter hook function is executed before tracking an event.
#### Props
- **ctx** : `Context` - The track context.
-
-#### Returns
-
-- `Adapter` - A new adapter instance.
+- **eventType** : `EventType` - The event type.
+- **eventData** : `EventData[EventType]` - The event data.
#### Example
```typescript title="AdapterBuilder.ts"
-const adapterBuilder = new AdapterBuilder({
- new ReportAdapter(),
-});
-
-const adapter = adapterBuilder.build(ctx);
+adapterBuilder.before(
+ (
+ ctx: TrackContext,
+ eventType: keyof EventDataOption,
+ eventData: EventDataOption[keyof EventDataOption]
+ ) => {
+ //do something
+ }
+);
```
-### `before`
+### `transform`
-The adapter hook function is executed before tracking an event.
+The adapter hook function is executed to transform the event data before tracking.
#### Props
-- **event** : `EventData` - The event data.
-
-#### Returns
-
-- `EventData` - The modified event data.
+- **eventType** : `EventType` - The event type.
+- **fun** : `(
+ ctx: Context,
+ eventType: Key,
+ eventData: LeftEventData[Key]
+) => AdapterReportData | Promise` - The transform function.
#### Example
```typescript title="AdapterBuilder.ts"
-const adapterBuilder = new AdapterBuilder({
- new ReportAdapter(),
-});
-
-adapterBuilder.before(event);
+adapterBuilder
+ .transform(
+ 'addCart',
+ (
+ ctx: TrackContext,
+ eventType: 'addCart',
+ eventData: EventDataOption['addCart']
+ ) => {
+ return {
+ ...eventData,
+ goodName: 'ac_' + eventData?.goodsName,
+ timeStamp: Date.now(),
+ };
+ }
+ )
+ .transform(
+ 'previewGoods',
+ (
+ ctx: TrackContext,
+ eventType: 'previewGoods',
+ eventData: EventDataOption['previewGoods']
+ ) => {
+ return {
+ ...eventData,
+ goodName: 'pg_' + eventData?.goodsName,
+ timeStamp: Date.now(),
+ };
+ }
+ );
```
-### `transform`
+### `after`
-The adapter hook function is executed to transform the event data before tracking.
+The adapter hook function is executed after report an event.
#### Props
-- **event** : `EventData` - The event data.
+- **ctx** : `Context` - The track context.
+- **eventType** : `EventType` - The event type.
+- **reportData** : `any` - The report data.
+
+#### Example
+
+```typescript title="AdapterBuilder.ts"
+adapterBuilder.after(
+ (
+ ctx: TrackContext,
+ eventType: keyof EventDataOption,
+ eventData: EventDataOption[keyof EventDataOption]
+ ) => {
+ //do something
+ }
+);
+```
+
+### `build`
+
+Builds a adapter instance.
#### Returns
-- `EventData` - The modified event data.
+- `Adapter` - Adapter instance.
#### Example
```typescript title="AdapterBuilder.ts"
-const adapterBuilder = new AdapterBuilder({
- new ReportAdapter(),
-});
-
-adapterBuilder.transform(event);
+const adapter = adapterBuilder.build();
```
-
-### `after`
diff --git a/website/docs/api/track-builder.md b/website/docs/api/track-builder.md
index ffc013b..16a7950 100644
--- a/website/docs/api/track-builder.md
+++ b/website/docs/api/track-builder.md
@@ -3,47 +3,114 @@
The `TrackBuilder` class is used to create a track from a list of waypoints. The waypoints are used to create a track that can be used to generate a path for a vehicle to follow.
```typescript title="Signature"
-
+class TrackBuilder<
+ Context extends TrackContext,
+ EventData extends TrackEventDataBase,
+> {
+ constructor(options: TrackCreateOptions = {});
+}
```
## Hooks
-### `before`
+### `init`
+
+#### Props
+
+- `options` - The options to create the track.
-`Function`
+#### Example
+
+```typescript title="TrackBuilder.ts"
+// method 1
+trackBuilder.init({ reportAdapter: adapter, consoleAdapter: adapter });
+// method 2
+trackBuilder.init(() => {
+ return { reportAdapter: adapter, consoleAdapter: adapter };
+});
+```
+
+### `before`
#### Props
-#### Returns
+- **ctx** : `Context` - The track context.
#### Example
-### `after`
+```typescript title="TrackBuilder.ts"
+trackBuilder.before(async (ctx: TrackContext) => {
+ // do something
+});
+```
-`Function`
+### `after`
#### Props
-#### Returns
+- **ctx** : `Context` - The track context.
#### Example
-### `select`
+```typescript title="TrackBuilder.ts"
+trackBuilder.after(async (ctx: TrackContext) => {
+ // do something
+});
+```
-`Function`
+### `select`
#### Props
-#### Returns
+- **ctx** : `Context` - The track context.
+- **adapterMap** : `Record>` - The adapter map.
#### Example
-### `track`
+```typescript title="TrackBuilder.ts"
+// method 1
+trackBuilder.select(['consoleAdapter', 'reportAdapter']);
+// method 2
+trackBuilder.select(
+ (
+ ctx: TrackContext,
+ adapterMap: {
+ reportAdapter: ReportAdapter;
+ consoleAdapter: ReportAdapter;
+ }
+ ) => ['consoleAdapter', 'reportAdapter']
+);
+// method 3
+trackBuilder.select(
+ (
+ ctx: TrackContext,
+ adapterMap: {
+ reportAdapter: ReportAdapter;
+ consoleAdapter: ReportAdapter;
+ }
+ ) => Promise.resolve(['consoleAdapter', 'reportAdapter'])
+);
+```
-`Function`
+### `track`
#### Props
-#### Returns
+- **eventType** : `addCart` - The event type.
+- **eventData** : `{
+ price: 99.99,
+ goodsId: 'g123',
+ goodsName: 'Sample Goods',
+ count: 2,
+}` - The event data.
#### Example
+
+```typescript title="TrackBuilder.ts"
+trackBuilder.track('addCart', {
+ price: 99.99,
+ goodsId: 'g123',
+ goodsName: 'Sample Goods',
+ count: 2,
+});
+```
diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx
index 89aa50c..a7467e5 100644
--- a/website/src/pages/index.tsx
+++ b/website/src/pages/index.tsx
@@ -5,7 +5,6 @@ import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { CheckIcon } from '@heroicons/react/24/outline';
import { DocumentDuplicateIcon } from '@heroicons/react/24/outline';
-import DynamicCoding from '@site/src/components/Amimate/DynamicCoding';
import HomepageFeatures from '@site/src/components/HomepageFeatures';
import LogoDark from '@site/static/img/logo-dark.svg';
import LogoWhite from '@site/static/img/logo-white.svg';
@@ -106,10 +105,6 @@ export default function Home() {
>
-
From cb4896cee2c327583b982302b7b57a1b07fbf893 Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Tue, 13 Aug 2024 22:14:35 +0800
Subject: [PATCH 08/28] docs: perfect docs
---
tests/test-track-docs.spec.ts | 3 ++-
website/docs/api/adapter-builder.md | 22 +++++++++++-----------
website/docs/api/base-adapter.md | 8 ++++----
website/docs/api/track-builder.md | 6 +++---
4 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/tests/test-track-docs.spec.ts b/tests/test-track-docs.spec.ts
index 02370da..59f515e 100644
--- a/tests/test-track-docs.spec.ts
+++ b/tests/test-track-docs.spec.ts
@@ -1,5 +1,6 @@
import { createAdapterBuilder } from '../src/adapter/create-adapter-builder.js';
import { createTrackBuilder } from '../src/core/create-track-builder.js';
+import { AdapterReportData } from '../src/types/types-adapter.js';
import { TrackContext } from '../src/types/types-create.js';
import { ReportAdapter } from './test-utils/adapter/report-adapter.js';
import { AdapterOptions } from './test-utils/types/type-adapter-options.js';
@@ -98,7 +99,7 @@ describe('test-track-pipeline.spec', () => {
(
ctx: TrackContext,
eventType: keyof EventDataOption,
- eventData: EventDataOption[keyof EventDataOption]
+ reportData: AdapterReportData
) => {
//do something
}
diff --git a/website/docs/api/adapter-builder.md b/website/docs/api/adapter-builder.md
index c761589..733b789 100644
--- a/website/docs/api/adapter-builder.md
+++ b/website/docs/api/adapter-builder.md
@@ -20,9 +20,9 @@ The setup data. It is often useful to extend the report method by configuring so
#### Props
-- **ctx** : `Context` - The track context.
-- **eventType** : `EventType` - The event type.
-- **eventData** : `EventData[EventType]` - The event data.
+- **ctx** : `TrackContext` - The track context.
+- **eventType** : `keyof EventDataOption` - The event type.
+- **eventData** : `EventDataOption[keyof EventDataOption]` - The event data.
#### Example
@@ -47,9 +47,9 @@ The adapter hook function is executed before tracking an event.
#### Props
-- **ctx** : `Context` - The track context.
-- **eventType** : `EventType` - The event type.
-- **eventData** : `EventData[EventType]` - The event data.
+- **ctx** : `TrackContext` - The track context.
+- **eventType** : `keyof EventDataOption` - The event type.
+- **eventData** : `EventDataOption[keyof EventDataOption]` - The event data.
#### Example
@@ -71,7 +71,7 @@ The adapter hook function is executed to transform the event data before trackin
#### Props
-- **eventType** : `EventType` - The event type.
+- **eventType** : `keyof EventDataOption` - The event type.
- **fun** : `(
ctx: Context,
eventType: Key,
@@ -118,9 +118,9 @@ The adapter hook function is executed after report an event.
#### Props
-- **ctx** : `Context` - The track context.
-- **eventType** : `EventType` - The event type.
-- **reportData** : `any` - The report data.
+- **ctx** : `TrackContext` - The track context.
+- **eventType** : `keyof EventDataOption` - The event type.
+- **reportData** : `AdapterReportData` - The report data.
#### Example
@@ -129,7 +129,7 @@ adapterBuilder.after(
(
ctx: TrackContext,
eventType: keyof EventDataOption,
- eventData: EventDataOption[keyof EventDataOption]
+ reportData: AdapterReportData
) => {
//do something
}
diff --git a/website/docs/api/base-adapter.md b/website/docs/api/base-adapter.md
index 4b4bf80..c5072fa 100644
--- a/website/docs/api/base-adapter.md
+++ b/website/docs/api/base-adapter.md
@@ -32,9 +32,9 @@ Checks if the adapter is available.
#### Props
-- **ctx** : `Context` - The track context.
-- **eventType** : `EventType` - The type of the event.
-- **eventData** : `EventData[EventType]` - The data associated with the event.
+- **ctx** : `TrackContext` - The track context.
+- **eventType** : `keyof EventDataOption` - The event type.
+- **eventData** : `EventData[keyof EventDataOption]` - The data associated with the event.
#### Returns
@@ -66,7 +66,7 @@ Reports the event to the adapter.
#### Props
-- **ctx** : `Context` - The track context.
+- **ctx** : `TrackContext` - The track context.
- **reportData** : `AdapterReportData` - The data to report.
- **setupData** : `Required['setup'] extends (...args: any) => any ? Awaited['setup']> : undefined` - The setup data. It is often useful to extend the report method by configuring some additional data to be used in the report phase without the transform processing
diff --git a/website/docs/api/track-builder.md b/website/docs/api/track-builder.md
index 16a7950..1ee0bbe 100644
--- a/website/docs/api/track-builder.md
+++ b/website/docs/api/track-builder.md
@@ -34,7 +34,7 @@ trackBuilder.init(() => {
#### Props
-- **ctx** : `Context` - The track context.
+- **ctx** : `TrackContext` - The track context.
#### Example
@@ -48,7 +48,7 @@ trackBuilder.before(async (ctx: TrackContext) => {
#### Props
-- **ctx** : `Context` - The track context.
+- **ctx** : `TrackContext` - The track context.
#### Example
@@ -62,7 +62,7 @@ trackBuilder.after(async (ctx: TrackContext) => {
#### Props
-- **ctx** : `Context` - The track context.
+- **ctx** : `TrackContext` - The track context.
- **adapterMap** : `Record>` - The adapter map.
#### Example
From 8994a8408a9fab0e072806e214e97c949c4dd7d2 Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Tue, 13 Aug 2024 22:21:53 +0800
Subject: [PATCH 09/28] docs: update docs
---
website/docs/api/base-adapter.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/docs/api/base-adapter.md b/website/docs/api/base-adapter.md
index c5072fa..9ce7596 100644
--- a/website/docs/api/base-adapter.md
+++ b/website/docs/api/base-adapter.md
@@ -62,7 +62,7 @@ export class ReportAdapter extends BaseAdapter<
`protected Function`
-Reports the event to the adapter.
+Method of reporting data to a third party, where the converted data can be obtained
#### Props
From 24298b98bc3ef3f9586cf332288b09842c6e6628 Mon Sep 17 00:00:00 2001
From: "shunquan.wang"
Date: Mon, 19 Aug 2024 22:00:26 +0800
Subject: [PATCH 10/28] feat: prefect docs
---
tests/test-track-docs.spec.ts | 142 ------------
tests/website-source/index.tsx | 33 +++
tests/website-source/report-adapter.ts | 32 +++
tests/website-source/track.ts | 60 +++++
tests/website-source/types.ts | 33 +++
website/.DS_Store | Bin 0 -> 6148 bytes
website/docs/api/adapter-builder.md | 91 +++++---
website/docs/api/base-adapter.md | 71 ++++--
website/docs/api/track-builder.md | 64 ++++--
website/docs/community/contributing.md | 145 +++++++++++-
website/docs/intro.md | 138 ------------
website/docs/intro/installation.md | 23 ++
website/docs/intro/introducing.md | 54 +++++
website/docs/intro/live-example.md | 21 ++
website/docs/intro/sample-example.md | 186 ++++++++++++++++
website/docusaurus.config.ts | 62 ++++--
website/package.json | 1 +
website/sidebars.ts | 29 ++-
.../src/components/Amimate/DynamicCoding.tsx | 37 ++--
.../src/components/Amimate/styles.module.css | 4 +
.../src/components/HomepageFeatures/index.tsx | 151 ++++++++++---
website/src/components/PreviewUML/index.tsx | 24 ++
website/src/components/Stackblitz/index.tsx | 2 +-
website/src/pages/index.module.css | 114 +++++++++-
website/src/pages/index.tsx | 15 +-
website/static/img/bars.svg | 1 +
website/static/img/browser.svg | 1 +
website/static/img/lightbulb.svg | 3 +
website/static/img/logo.svg | 208 +-----------------
website/static/img/muscle.svg | 1 +
website/static/img/puzzle.svg | 1 +
website/static/img/shield.svg | 4 +
website/static/img/uml-dark.png | Bin 0 -> 124573 bytes
website/static/img/uml-light.png | Bin 0 -> 119626 bytes
website/static/media/code.mp4 | Bin 0 -> 10960713 bytes
yarn.lock | 195 +++++++++++++++-
36 files changed, 1322 insertions(+), 624 deletions(-)
delete mode 100644 tests/test-track-docs.spec.ts
create mode 100644 tests/website-source/index.tsx
create mode 100644 tests/website-source/report-adapter.ts
create mode 100644 tests/website-source/track.ts
create mode 100644 tests/website-source/types.ts
create mode 100644 website/.DS_Store
delete mode 100644 website/docs/intro.md
create mode 100644 website/docs/intro/installation.md
create mode 100644 website/docs/intro/introducing.md
create mode 100644 website/docs/intro/live-example.md
create mode 100644 website/docs/intro/sample-example.md
create mode 100644 website/src/components/Amimate/styles.module.css
create mode 100644 website/src/components/PreviewUML/index.tsx
create mode 100644 website/static/img/bars.svg
create mode 100644 website/static/img/browser.svg
create mode 100644 website/static/img/lightbulb.svg
create mode 100644 website/static/img/muscle.svg
create mode 100644 website/static/img/puzzle.svg
create mode 100644 website/static/img/shield.svg
create mode 100644 website/static/img/uml-dark.png
create mode 100644 website/static/img/uml-light.png
create mode 100644 website/static/media/code.mp4
diff --git a/tests/test-track-docs.spec.ts b/tests/test-track-docs.spec.ts
deleted file mode 100644
index 59f515e..0000000
--- a/tests/test-track-docs.spec.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { createAdapterBuilder } from '../src/adapter/create-adapter-builder.js';
-import { createTrackBuilder } from '../src/core/create-track-builder.js';
-import { AdapterReportData } from '../src/types/types-adapter.js';
-import { TrackContext } from '../src/types/types-create.js';
-import { ReportAdapter } from './test-utils/adapter/report-adapter.js';
-import { AdapterOptions } from './test-utils/types/type-adapter-options.js';
-import { EventDataOption } from './test-utils/types/type-event.js';
-import { TrackData } from './test-utils/types/type-track-data.js';
-
-describe('test-track-pipeline.spec', () => {
- const trackData: TrackData = {
- bizMode: 'test',
- env: 'prod',
- platform: 'android',
- ip: '0.0.0.0',
- userId: 'uuid_10001',
- };
-
- const eventData: EventDataOption = {
- registry: {
- userName: 'testUser',
- mobile: '1234567890',
- pwd: 'password123',
- email: 'testuser@example.com',
- },
- previewGoods: {
- goodsId: 'g123',
- goodsName: 'Sample Goods',
- },
- addCart: {
- price: 99.99,
- goodsId: 'g123',
- goodsName: 'Sample Goods',
- count: 2,
- },
- timeStamp: '1000',
- };
-
- it('docs example', async () => {
- const adapter = new ReportAdapter();
-
- const adapterBuilder = createAdapterBuilder<
- TrackContext,
- EventDataOption,
- AdapterOptions, EventDataOption>
- >(adapter);
-
- adapterBuilder
- .setup(
- (
- ctx: TrackContext,
- eventType: keyof EventDataOption,
- eventData: EventDataOption[keyof EventDataOption]
- ) => {
- return Promise.resolve({
- name: 'setup',
- timeStamp: Date.now(),
- });
- }
- )
- .before(
- (
- ctx: TrackContext,
- eventType: keyof EventDataOption,
- eventData: EventDataOption[keyof EventDataOption]
- ) => {
- //do something
- }
- )
- .transform(
- 'addCart',
- (
- ctx: TrackContext,
- eventType: 'addCart',
- eventData: EventDataOption['addCart']
- ) => {
- return {
- ...eventData,
- goodName: 'ac_' + eventData?.goodsName,
- timeStamp: Date.now(),
- };
- }
- )
- .transform(
- 'previewGoods',
- (
- ctx: TrackContext,
- eventType: 'previewGoods',
- eventData: EventDataOption['previewGoods']
- ) => {
- return {
- ...eventData,
- goodName: 'pg_' + eventData?.goodsName,
- timeStamp: Date.now(),
- };
- }
- )
- .after(
- (
- ctx: TrackContext,
- eventType: keyof EventDataOption,
- reportData: AdapterReportData
- ) => {
- //do something
- }
- )
- .build();
-
- const trackBuilder = createTrackBuilder<
- TrackContext,
- EventDataOption
- >();
-
- await trackBuilder
- .init(() => {
- return { reportAdapter: adapter, consoleAdapter: adapter };
- })
- .before(async (ctx) => {
- // do something
- })
- .after(async (ctx) => {
- // do something
- })
- .select(
- (
- ctx: TrackContext,
- adapterMap: {
- reportAdapter: ReportAdapter;
- consoleAdapter: ReportAdapter;
- }
- ) => 'consoleAdapter'
- )
- .track('addCart', {
- price: 99.99,
- goodsId: 'g123',
- goodsName: 'Sample Goods',
- count: 2,
- });
-
- expect(true).toBeTruthy();
- });
-});
diff --git a/tests/website-source/index.tsx b/tests/website-source/index.tsx
new file mode 100644
index 0000000..e99352d
--- /dev/null
+++ b/tests/website-source/index.tsx
@@ -0,0 +1,33 @@
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+//@ts-nocheck
+import { reportTrack } from './track.js';
+
+export const Index = () => {
+ const onAddToCart = async () => {
+ await reportTrack().select('reportAdapter').track('addCart', {
+ price: 25.99,
+ goodsId: '23432252',
+ goodsName: 'Long Chair',
+ count: 1,
+ });
+ };
+ return (
+
+
+
+
+
+
+
Long Chair
+
ID: 23432252
+
+
$25.99
+
+
+ Add to cart
+
+
+
+
+ );
+};
diff --git a/tests/website-source/report-adapter.ts b/tests/website-source/report-adapter.ts
new file mode 100644
index 0000000..805382c
--- /dev/null
+++ b/tests/website-source/report-adapter.ts
@@ -0,0 +1,32 @@
+import { BaseAdapter } from '../../src/index.js';
+import { AdapterReportData } from '../../src/types/types-adapter.js';
+import { TrackContext } from '../../src/types/types-create.js';
+import {
+ ReportAdapterOptions,
+ ReportEventData,
+ ReportTrackData,
+} from './types.js';
+
+export class ReportAdapter extends BaseAdapter<
+ TrackContext,
+ ReportEventData,
+ ReportAdapterOptions, ReportEventData>
+> {
+ isTrackable(
+ ctx: TrackContext,
+ eventType: EventType,
+ eventData: ReportEventData[EventType]
+ ): boolean | Promise {
+ return true;
+ }
+
+ protected report(
+ ctx: TrackContext,
+ reportData: AdapterReportData,
+ setupData?:
+ | { name: 'setup' | 'setup2' | 'setup3'; timeStamp: number }
+ | undefined
+ ): void | Promise {
+ console.log('report', ctx, reportData, setupData);
+ }
+}
diff --git a/tests/website-source/track.ts b/tests/website-source/track.ts
new file mode 100644
index 0000000..ceb5e58
--- /dev/null
+++ b/tests/website-source/track.ts
@@ -0,0 +1,60 @@
+import { createAdapterBuilder, createTrackBuilder } from '../../src/index.js';
+import { TrackContext } from '../../src/types/types-create.js';
+import { ReportAdapter } from './report-adapter.js';
+import {
+ ReportAdapterOptions,
+ ReportEventData,
+ ReportTrackData,
+} from './types.js';
+
+export const reportTrack = () => {
+ const reportAdapter = new ReportAdapter();
+
+ const adapterBuilder = createAdapterBuilder<
+ TrackContext,
+ ReportEventData,
+ ReportAdapterOptions, ReportEventData>
+ >(reportAdapter);
+
+ const adapter = adapterBuilder
+ .setup(() => {
+ return Promise.resolve({
+ name: 'setup',
+ timeStamp: Date.now(),
+ });
+ })
+ .before((ctx, eventType, eventData) => {
+ console.log('before', ctx, eventType, eventData);
+ })
+ .transform('addCart', (ctx, eventType, eventData) => {
+ return {
+ ...eventData,
+ goodName: 'ac_' + eventData?.goodsName,
+ };
+ })
+ .transform('registry', (ctx, eventType, eventData) => {
+ return { ...eventData, userName: 'rg_' + eventData?.userName };
+ })
+ .after((ctx, eventType, reportData) => {
+ console.log('after', ctx, eventType, reportData);
+ })
+ .build();
+
+ const trackBuilder = createTrackBuilder<
+ TrackContext,
+ ReportEventData
+ >();
+
+ return trackBuilder
+ .init(() => {
+ return {
+ reportAdapter: adapter,
+ };
+ })
+ .before((ctx) => {
+ console.log('before track', ctx);
+ })
+ .after((ctx) => {
+ console.log('after track', ctx);
+ });
+};
diff --git a/tests/website-source/types.ts b/tests/website-source/types.ts
new file mode 100644
index 0000000..ef02436
--- /dev/null
+++ b/tests/website-source/types.ts
@@ -0,0 +1,33 @@
+export type ReportAdapterOptions = {
+ setup?: (
+ ctx: Context,
+ eventTYpe: EventType,
+ eventData: EventData[EventType]
+ ) => Promise<{
+ name: 'setup' | 'setup2' | 'setup3';
+ timeStamp: number;
+ }>;
+};
+
+export type ReportTrackData = {
+ bizMode: 'test' | 'test2';
+ env: 'prod' | 'uat';
+ platform: 'android' | 'ios';
+ ip: string;
+ userId: string;
+};
+
+export type ReportEventData = {
+ registry?: {
+ userName: string;
+ mobile: string;
+ pwd: string;
+ email: string;
+ };
+ addCart?: {
+ price: number;
+ goodsId: string;
+ goodsName: string;
+ count: number;
+ };
+};
diff --git a/website/.DS_Store b/website/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6
GIT binary patch
literal 6148
zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3
zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ
zLs35+`xjp>T0`
+
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
+
+- **eventType** : `keyof EventData`
-#### Props
+ The type of event being tracked. This is usually a key from the EventData that corresponds to specific events like click, purchase, etc.
-- **ctx** : `TrackContext` - The track context.
-- **eventType** : `keyof EventDataOption` - The event type.
-- **eventData** : `EventDataOption[keyof EventDataOption]` - The event data.
+- **eventData** : `EventData[keyof EventData]`
+
+ The data associated with the event. This contains all relevant information for the specific event type.
#### Example
@@ -30,8 +40,8 @@ The setup data. It is often useful to extend the report method by configuring so
adapterBuilder.setup(
(
ctx: TrackContext,
- eventType: keyof EventDataOption,
- eventData: EventDataOption[keyof EventDataOption]
+ eventType: keyof EventData,
+ eventData: EventData[keyof EventData]
) => {
return Promise.resolve({
name: 'setup',
@@ -43,13 +53,21 @@ adapterBuilder.setup(
### `before`
-The adapter hook function is executed before tracking an event.
+The `before` hook is executed before tracking an event. This is where you can perform any necessary preprocessing or validation.
+
+#### Parameters
+
+- **ctx** : `TrackContext`
+
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
+
+- **eventType** : `keyof EventData`
-#### Props
+ The type of event being tracked. This is usually a key from the EventData that corresponds to specific events like click, purchase, etc.
-- **ctx** : `TrackContext` - The track context.
-- **eventType** : `keyof EventDataOption` - The event type.
-- **eventData** : `EventDataOption[keyof EventDataOption]` - The event data.
+- **eventData** : `EventData[keyof EventData]`
+
+ The data associated with the event. This contains all relevant information for the specific event type.
#### Example
@@ -57,8 +75,8 @@ The adapter hook function is executed before tracking an event.
adapterBuilder.before(
(
ctx: TrackContext,
- eventType: keyof EventDataOption,
- eventData: EventDataOption[keyof EventDataOption]
+ eventType: keyof EventData,
+ eventData: EventData[keyof EventData]
) => {
//do something
}
@@ -67,16 +85,21 @@ adapterBuilder.before(
### `transform`
-The adapter hook function is executed to transform the event data before tracking.
+The `transform` hook allows you to modify the event data before it is sent to the tracking system. You can use this hook to change, enrich, or sanitize the event data.
+
+#### Parameters
+
+- **eventType** : `keyof EventData`
-#### Props
+ The type of event being tracked. This is usually a key from the EventData that corresponds to specific events like click, purchase, etc.
-- **eventType** : `keyof EventDataOption` - The event type.
- **fun** : `(
ctx: Context,
eventType: Key,
eventData: LeftEventData[Key]
-) => AdapterReportData | Promise` - The transform function.
+) => AdapterReportData | Promise`
+
+ The function to transform the event data.
#### Example
@@ -87,7 +110,7 @@ adapterBuilder
(
ctx: TrackContext,
eventType: 'addCart',
- eventData: EventDataOption['addCart']
+ eventData: EventData['addCart']
) => {
return {
...eventData,
@@ -101,7 +124,7 @@ adapterBuilder
(
ctx: TrackContext,
eventType: 'previewGoods',
- eventData: EventDataOption['previewGoods']
+ eventData: EventData['previewGoods']
) => {
return {
...eventData,
@@ -114,13 +137,21 @@ adapterBuilder
### `after`
-The adapter hook function is executed after report an event.
+The `after` hook is executed after the event has been reported. This is where you can perform any post-processing, such as logging or triggering additional actions based on the reported data.
+
+#### Parameters
-#### Props
+- **ctx** : `TrackContext`
-- **ctx** : `TrackContext` - The track context.
-- **eventType** : `keyof EventDataOption` - The event type.
-- **reportData** : `AdapterReportData` - The report data.
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
+
+- **eventType** : `keyof EventData`
+
+ The type of event being tracked. This is usually a key from the EventData that corresponds to specific events like click, purchase, etc.
+
+- **reportData** : `AdapterReportData`
+
+ The data that needs to be reported. This can include the event type, associated data, and any additional metadata that should be sent to the third-party service.
#### Example
@@ -128,7 +159,7 @@ The adapter hook function is executed after report an event.
adapterBuilder.after(
(
ctx: TrackContext,
- eventType: keyof EventDataOption,
+ eventType: keyof EventData,
reportData: AdapterReportData
) => {
//do something
@@ -138,11 +169,13 @@ adapterBuilder.after(
### `build`
-Builds a adapter instance.
+The `build` method finalizes the adapter configuration and creates an instance of the adapter.
#### Returns
-- `Adapter` - Adapter instance.
+- `Adapter`
+
+ The configured adapter instance.
#### Example
diff --git a/website/docs/api/base-adapter.md b/website/docs/api/base-adapter.md
index 9ce7596..3e53be8 100644
--- a/website/docs/api/base-adapter.md
+++ b/website/docs/api/base-adapter.md
@@ -1,6 +1,15 @@
# BaseAdapter
-BaseAdapter is an abstract class that serves as the base for implementing track adapters. It provides common functionality and hooks for tracking events.
+`BaseAdapter` is an abstract class that serves as the foundation for creating track adapters. It provides common functionality and hooks for tracking events, allowing you to extend and customize behavior for specific use cases.
+
+## Overview
+
+The `BaseAdapter` class defines two key methods:
+
+- isTrackable: This method checks if a particular event should be tracked.
+- report: This method handles the reporting of event data to a third-party service or system.
+
+Both methods are meant to be extended in a concrete implementation of the `BaseAdapter` class.
```typescript title="Signature"
export interface TrackAdapter<
@@ -26,32 +35,40 @@ export interface TrackAdapter<
### `isTrackable`
-`abstract Function`
+`isTrackable` is an abstract method that checks whether a specific event should be tracked by the adapter. This method must be implemented in any subclass of `BaseAdapter`.
+
+#### Parameters
+
+- **ctx** : `TrackContext`
+
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
+
+- **eventType** : `keyof EventData`
-Checks if the adapter is available.
+ The type of event being tracked. This is usually a key from the EventData that corresponds to specific events like click, purchase, etc.
-#### Props
+- **eventData** : `EventData[keyof EventData]`
-- **ctx** : `TrackContext` - The track context.
-- **eventType** : `keyof EventDataOption` - The event type.
-- **eventData** : `EventData[keyof EventDataOption]` - The data associated with the event.
+ The data associated with the event. This contains all relevant information for the specific event type.
#### Returns
-- `boolean | Promise` - A boolean indicating if the adapter is available.
+- `boolean | Promise` - A boolean or a promise that resolves to a boolean, indicating whether the event should be tracked. `true` means the event is trackable, while `false` means it is not.
#### Example
+Here’s an example implementation of `isTrackable` that only tracks `addCart` events:
+
```typescript title="ReportAdapter.ts"
export class ReportAdapter extends BaseAdapter<
TrackContext,
- EventDataOption,
- AdapterOptions, EventDataOption>
+ EventData,
+ AdapterOptions, EventData>
> {
- isTrackable(
+ isTrackable(
ctx: TrackContext,
eventType: EventType,
- eventData: EventDataOption[EventType]
+ eventData: EventData[EventType]
): boolean | Promise {
return eventType === 'addCart';
}
@@ -60,32 +77,42 @@ export class ReportAdapter extends BaseAdapter<
### `report`
-`protected Function`
+`report` is a protected method used to send event data to an external system or service. This method can be overridden in a subclass to customize the reporting process, such as transforming the data or adding additional logic before the report is sent.
+
+#### Parameters
+
+- **ctx** : `TrackContext`
-Method of reporting data to a third party, where the converted data can be obtained
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
-#### Props
+- **reportData** : `AdapterReportData`
-- **ctx** : `TrackContext` - The track context.
-- **reportData** : `AdapterReportData` - The data to report.
-- **setupData** : `Required['setup'] extends (...args: any) => any ? Awaited['setup']> : undefined` - The setup data. It is often useful to extend the report method by configuring some additional data to be used in the report phase without the transform processing
+ The data that needs to be reported. This can include the event type, associated data, and any additional metadata that should be sent to the third-party service.
+
+- **setupData** : `Required['setup'] extends (...args: any) => any ? Awaited['setup']> : undefined` (Optional)
+
+ The setup data, which may include configurations or preliminary data used during the report phase. This allows for additional data processing or setup before the reporting occurs.
#### Returns
-- `void | Promise` - A void or a promise that resolves to void.
+- `void | Promise`
+
+ This method can either return `void` or a `Promise` that resolves to `void`, depending on whether the reporting process is asynchronous.
#### Example
+Here’s an example implementation of the report method:
+
```typescript title="ReportAdapter.ts"
export class ReportAdapter extends BaseAdapter<
TrackContext,
- EventDataOption,
- AdapterOptions, EventDataOption>
+ EventData,
+ AdapterOptions, EventData>
> {
report(
ctx: TrackContext,
reportData: AdapterReportData,
- setupData?: Awaited, EventDataOption>['setup']>
+ setupData?: Awaited, EventData>['setup']>
): void | Promise {
// do something
}
diff --git a/website/docs/api/track-builder.md b/website/docs/api/track-builder.md
index 1ee0bbe..e45521d 100644
--- a/website/docs/api/track-builder.md
+++ b/website/docs/api/track-builder.md
@@ -1,6 +1,15 @@
# TrackBuilder
-The `TrackBuilder` class is used to create a track from a list of waypoints. The waypoints are used to create a track that can be used to generate a path for a vehicle to follow.
+The `TrackBuilder` class is designed to create a track from a series of waypoints. It provides a structured way to generate a path for a vehicle or an event tracking system to follow. This class includes hooks that allow you to customize the behavior of the track creation process.
+
+## Overview
+
+The `TrackBuilder` class allows you to define and customize the process of building a track by:
+
+- Initializing the track with specific options.
+- Adding hooks to execute custom logic before and after certain stages of the track building.
+- Selecting specific adapters for tracking events.
+- Tracking events with custom data.
```typescript title="Signature"
class TrackBuilder<
@@ -11,13 +20,23 @@ class TrackBuilder<
}
```
+### Constructor
+
+- options: `TrackCreateOptions` (Optional)
+
+ The options used to create the track. These options typically include configurations for the tracking adapters, context, and other relevant settings.
+
## Hooks
### `init`
-#### Props
+The `init` method is used to initialize the track builder with specific options, such as which adapters to use for reporting and console output.
-- `options` - The options to create the track.
+#### Parameters
+
+- options: `TrackCreateOptions`
+
+ An object containing the initialization options for the track. This typically includes the adapters and other configurations necessary to build the track.
#### Example
@@ -32,9 +51,13 @@ trackBuilder.init(() => {
### `before`
+The `before` method is a hook that allows you to execute custom logic before the track building process begins. This can be used for tasks such as preprocessing, validation, or logging.
+
#### Props
-- **ctx** : `TrackContext` - The track context.
+- **ctx** : `TrackContext`
+
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
#### Example
@@ -46,9 +69,13 @@ trackBuilder.before(async (ctx: TrackContext) => {
### `after`
+The `after` method is a hook that allows you to execute custom logic after the track building process is completed. This can be used for tasks such as cleanup, final validation, or post-processing.
+
#### Props
-- **ctx** : `TrackContext` - The track context.
+- **ctx** : `TrackContext`
+
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
#### Example
@@ -60,10 +87,17 @@ trackBuilder.after(async (ctx: TrackContext) => {
### `select`
+The `select` method is used to choose which adapters should be used during the track creation process. This allows for dynamic selection based on the context or specific conditions.
+
#### Props
-- **ctx** : `TrackContext` - The track context.
-- **adapterMap** : `Record>` - The adapter map.
+- **ctx** : `TrackContext`
+
+ The context in which the tracking is occurring. This typically includes details such as user information, environment, or other contextual data relevant to the tracking event.
+
+- **adapterMap** : `Record>`
+
+ A map of available adapters, where the key is the adapter name and the value is the adapter instance.
#### Example
@@ -94,15 +128,17 @@ trackBuilder.select(
### `track`
+The `track` method is used to track an event by specifying the event type and associated event data. This method triggers the tracking process, using the previously selected adapters.
+
#### Props
-- **eventType** : `addCart` - The event type.
-- **eventData** : `{
- price: 99.99,
- goodsId: 'g123',
- goodsName: 'Sample Goods',
- count: 2,
-}` - The event data.
+- **eventType** : `addCart`
+
+ The type of event being tracked. This is usually a key from the EventData that corresponds to specific events like click, purchase, etc.
+
+- **eventData** : `EventData[keyof EventData]`
+
+ An object containing the data associated with the event. This data usually includes information such as item details, pricing, quantity, and other relevant attributes.
#### Example
diff --git a/website/docs/community/contributing.md b/website/docs/community/contributing.md
index e3c5a93..5251f1a 100644
--- a/website/docs/community/contributing.md
+++ b/website/docs/community/contributing.md
@@ -1,5 +1,144 @@
-# contributing
+## All contributions are welcome!
-All contributions are welcome!
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
-Please take a moment to review guidelines [PR](https://github.com/hyperse-io/track/pulls) | [Issues](https://github.com/hyperse-io/track/issues)
+:::info
+
+Thank you for your interest in contributing to our documentation site! We appreciate your support and value the insights and expertise of our community. This page outlines the guidelines and process for contributing, as well as the rewards you can earn for your efforts.
+
+:::
+
+We welcome contributions in a variety of forms, including but not limited to:
+
+1. Pointing out a mistake/typo and (optionally) providing a solution
+2. Providing translation
+3. Creating or improving diagrams, charts, or visual aids
+4. Suggesting improvements to the documentation structure or organization
+5. Writing or updating a tutorial or guide
+6. Creating or updating code samples, examples, or demos
+7. Polishing or improving document writing
+
+To make a contribution, please take one of the following actions:
+
+- **Report an Issue**: If you spot a problem or is willing to suggest improvements, [create an issue](https://github.com/hyperse-io/track/issues/new/choose) to let us know.
+- **Submit Changes**: For direct contributions to content, [create a pull request](https://github.com/hyperse-io/track/compare).
+
+## Create an Issue
+
+You can create an issue for the following purposes:
+
+- To report any mistakes or typos.
+- To request new content or improvements to current content.
+
+You can typically create an issue directly through the [GitHub web page](https://github.com/hyperse-io/track/issues/new/choose). Here, you'll find various templates to guide your issue submission.
+
+If you're able to address the issue yourself, we encourage you to take the initiative. When creating an issue, you can indicate your willingness to resolve it. For bug reports, select the option “I'd be willing to fix this issue myself” in the BUG template. For feature requests, select “I'd be willing to contribute this feature myself” in the Feature Request template.
+
+
+
+
+**Describe the bug**
+
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+
+Steps to reproduce the behavior:
+
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+
+A clear and concise description of what you expected to happen.
+
+**Environment (please complete the following information):**
+
+- @hyperse/track version:
+- Nodejs version
+
+**Additional context**
+
+Add any other context about the problem here.
+
+
+
+**Is your feature request related to a problem? Please describe.**
+
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+
+Add any other context or screenshots about the feature request here.
+
+
+
+
+## Create a Pull Request
+
+Contributing to projects through a pull request (PR) is a valuable way to improve existing documentation or code. This guide will walk you through the process step by step, ensuring clarity and ease of understanding, especially for those new to GitHub and git operations.
+
+For complex operations, you can also check the [closed pull requests](https://github.com/hyperse-io/track/pulls?q=is%3Apr+is%3Aclosed) for grammar reference. For example,
+
+- [Added Ecosystem Page + Video](https://github.com/hyperse-io/track/pull/392) shows how to add an article with video reference.
+- [feat: template example](https://github.com/hyperse-io/track/pull/410) shows how to make use of mdx's feature to create similar pages using template.
+
+:::note
+
+This process does not apply to [TRANSLATIONS](#provide-translation).
+
+:::
+
+### Local Development for Substantial Changes
+
+:::tip
+
+Refer to [Github's tutorial](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project) if you are not familiar with git or Github operations.
+
+:::
+
+For more significant contributions like adding a new page or extensive revisions:
+
+1. Setting Up:
+
+ - Ensure you have [node.js](https://nodejs.org/en) (version `>= 18`) and [yarn](https://yarnpkg.com/getting-started/install) installed.
+ - Fork and clone the [hyperse-io/track](https://github.com/hyperse-io/track)repository. Detailed instructions for forking and cloning are available on GitHub's help pages.
+
+2. Making Changes Locally:
+
+- Run `yarn` in your terminal to preview the project.
+- Complete the place you need to change and provide the corresponding UT.
+- Run `yarn test` command to check whether the function is complete.
+- Navigate to the `website/**` folder and complete documentation for new features.
+
+3. Submitting Your Changes:
+
+- After making changes, run `npx changeset add` to improve the changelog.
+- Commit your changes with a meaningful message, then push to your forked repository. Initiate a pull request on GitHub by comparing your branch to the original repository.
+
+### Working on an Existing Issue
+
+To avoid overlapping efforts and streamline contributions, it is suggested to follow these steps:
+
+1. Check for Accepted Issues:
+
+- Look for issues labeled "ACCEPTED" or similarly indicating readiness for contributions. If unsure, ask in the issue comments.
+
+2. Announce Your Intentions:
+
+- Comment on the issue stating that you are working on it. This helps prevent duplicate efforts.
+
+3. (Optional)Link Your Contributions:
+
+- When committing your changes, reference the issue number in your commit message, e.g., `fix: typo. Ref #123456`.
diff --git a/website/docs/intro.md b/website/docs/intro.md
deleted file mode 100644
index 4e1f1e2..0000000
--- a/website/docs/intro.md
+++ /dev/null
@@ -1,138 +0,0 @@
----
-sidebar_position: 1
-slug: /getting-started
----
-
-# Getting Started
-
-## Install
-
-```bash
-npm install @hyperse/track --save
-```
-
-or you can use **yarn**
-
-```bash
-yarn add @hyperse/track
-```
-
-We have completed installing the package.
-
-## Simple usage
-
-Here is a simple usage for using the component:
-
-```ts
-export type Context = {
- env: 'prod' | 'uat';
- platform: 'android' | 'ios';
- ip: string;
- userId: string;
-};
-
-export type EventData = {
- registry: {
- userName: string;
- mobile: string;
- pwd: string;
- email: string;
- };
- addCart: {
- price: number;
- goodsId: string;
- goodsName: string;
- count: number;
- };
-};
-
-export type AdapterOptions = {
- setup?: (
- ctx: Context,
- eventData: EventData[keyof EventData]
- ) => Promise<{
- name: 'setup' | 'setup1' | 'setup2';
- timeStamp: number;
- }>;
-};
-
-// custom report adapter
-export class ReportAdapter extends BaseAdapter<
- TrackContext,
- EventData,
- AdapterOptions, EventData>
-> {
- isTrackable(
- ctx: TrackContext,
- eventType: EventType,
- eventData: EventData[EventType]
- ): boolean | Promise {
- return true;
- }
- report(
- ctx: TrackContext,
- reportData: AdapterReportData,
- setupData?:
- | {
- name: 'setup' | 'setup1' | 'setup2';
- timeStamp: number;
- }
- | undefined
- ): void | Promise {}
-}
-
-const reportAdapter = new ReportAdapter();
-
-// create adapter builder
-const adapterBuilder = createAdapterBuilder<
- TrackContext,
- EventData,
- AdapterOptions, EventData>
->(reportAdapter);
-
-// mount adapter hook
-adapterBuilder
- .setup((ctx, eventData) => {
- return Promise.resolve({
- name: 'setup' as const,
- timeStamp: new Date().getTime(),
- newField: 'newField',
- });
- })
- .before(async (ctx, eventType, eventData) => {
- console.log('before');
- })
- .transform('addCart', (ctx, eventType, eventData) => {
- return {
- ...eventData,
- pay: {
- payId: 'p123',
- payName: 'Sample Pay',
- payType: 'credit',
- },
- timeStamp: '2024-09-01T00:00:00Z',
- };
- })
- .after(async (ctx, eventType, eventData) => {
- console.log('after', eventData);
- })
- .build();
-
-// create track builder
-const trackBuilder = createTrackBuilder<
- TrackContext,
- EventDataOption
->();
-
-// mount track hook
-await trackBuilder
- .init({ reportAdapter: reportAdapter })
- .before(async (ctx) => {})
- .after(async (ctx) => {})
- .select(() => ['reportAdapter'])
- .track('addCart', eventData.addCart);
-```
-
-## Congratulations !
-
-That's all, now let's deep dive into the [props](/docs/api-reference).
diff --git a/website/docs/intro/installation.md b/website/docs/intro/installation.md
new file mode 100644
index 0000000..d22cf11
--- /dev/null
+++ b/website/docs/intro/installation.md
@@ -0,0 +1,23 @@
+# Installation
+
+## Requirements
+
+- [Node.js](https://nodejs.org/en/) v18 or above, with support for even-numbered Node.js versions.
+
+## Install
+
+```bash
+npm install @hyperse/track --save
+```
+
+or you can use **yarn**
+
+```bash
+yarn add @hyperse/track
+```
+
+We have completed installing the package.
+
+## Congratulations !
+
+That's all, now let's deep dive into the [props](/docs/api/base-adapter).
diff --git a/website/docs/intro/introducing.md b/website/docs/intro/introducing.md
new file mode 100644
index 0000000..a2cbc68
--- /dev/null
+++ b/website/docs/intro/introducing.md
@@ -0,0 +1,54 @@
+# Introducing Track
+
+import PreviewUML from '@site/src/components/PreviewUML';
+
+## What is Track ?
+
+### Track: A Modern TypeScript Library for Data Collection
+
+**Track** is a sophisticated TypeScript library designed specifically for efficient and versatile data collection in web applications. With a focus on modern development practices, Track incorporates several powerful features, making it an ideal choice for developers seeking a reliable and scalable solution for front-end data tracking.
+
+
+
+### Key Features of Track
+
+1. **TypeScript-Based Development**:
+
+ - **Track** is built using TypeScript, which provides strong typing and advanced development tools. This ensures that developers can catch errors during development, resulting in more reliable and maintainable code.
+
+2. **Strong Typing and Type Inference**:
+
+ - Track fully supports TypeScript's type inference, allowing developers to benefit from strong typing without having to manually specify types everywhere. This reduces runtime errors and improves overall code stability.
+
+3. **Smart Data Collection**:
+
+ - Track integrates intelligent data collection strategies that optimize the process based on user behavior and changing data. This ensures that the data collected is both accurate and timely, without overloading the front-end application.
+
+4. **Scalable Design**:
+
+ - Whether you're working on a small project or a large-scale application, Track is designed to scale effortlessly. Its architecture supports the collection of vast amounts of data without compromising performance, making it suitable for applications of all sizes.
+
+5. **Powerful and Flexible**:
+
+ - Track is equipped with a robust set of features that allow it to handle a wide variety of data sources. From DOM elements to user inputs and network requests, Track can collect and process data from multiple sources with ease. Its flexibility allows developers to customize collection parameters, such as frequency, format, and filtering rules, to meet specific application needs.
+
+6. **Front-End Focused**:
+
+ - Designed with the front-end in mind, Track is lightweight and optimized for performance. It collects data efficiently without affecting the user experience, making it an excellent choice for modern web applications where performance is critical.
+
+7. **Easy Integration**:
+ - Track provides an intuitive API that seamlessly integrates with popular front-end frameworks like React, Vue, and Angular. This makes it easy to add data collection capabilities to existing projects without disrupting the current workflow.
+
+### Use Cases for Track
+
+- **User Behavior Analysis**: Track can be used to gather detailed information about how users interact with your web application. By tracking clicks, scrolls, form submissions, and other actions, you can gain insights into user behavior and optimize your application accordingly.
+
+- **Performance Monitoring**: Track allows you to monitor the performance of your web application in real-time. By collecting data on page load times, resource usage, and other metrics, you can quickly identify and address performance bottlenecks.
+
+- **A/B Testing**: With Track, you can easily implement A/B testing by collecting data from different versions of your web pages. This helps you make data-driven decisions by comparing how users interact with each version.
+
+- **Event Tracking**: Track makes it easy to set up custom event tracking in your application. Whether you need to monitor specific user actions or track conversions, Track provides the tools you need to capture and analyze the data.
+
+## Congratulations !
+
+That's all, now let's deep dive into the [props](/docs/api/base-adapter).
diff --git a/website/docs/intro/live-example.md b/website/docs/intro/live-example.md
new file mode 100644
index 0000000..3baba30
--- /dev/null
+++ b/website/docs/intro/live-example.md
@@ -0,0 +1,21 @@
+# Example
+
+import Stackblitz from '../../src/components/Stackblitz';
+
+## Live usage
+
+Below is the complete usage of the component, representing a real business scenario:
+
+---
+
+:::info
+
+Read more in [**User guide > introducing**](/docs/intro/introducing)
+
+:::
+
+
+
+## Congratulations !
+
+That's all, now let's deep dive into the [props](/docs/api/base-adapter).
diff --git a/website/docs/intro/sample-example.md b/website/docs/intro/sample-example.md
new file mode 100644
index 0000000..8798246
--- /dev/null
+++ b/website/docs/intro/sample-example.md
@@ -0,0 +1,186 @@
+# Example
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Simple usage
+
+Here is a simple usage for using the component:
+
+---
+
+:::info
+
+Read more in [**User guide > introducing**](/docs/intro/introducing)
+
+:::
+
+
+
+
+
+```typescript title="track.ts"
+export const reportTrack = () => {
+ const reportAdapter = new ReportAdapter();
+
+ const adapterBuilder = createAdapterBuilder<
+ TrackContext,
+ ReportEventData,
+ ReportAdapterOptions, ReportEventData>
+ >(reportAdapter);
+
+ const adapter = adapterBuilder
+ .setup(() => {
+ return Promise.resolve({
+ name: 'setup',
+ timeStamp: Date.now(),
+ });
+ })
+ .before((ctx, eventType, eventData) => {
+ console.log('before', ctx, eventType, eventData);
+ })
+ .transform('addCart', (ctx, eventType, eventData) => {
+ return {
+ ...eventData,
+ goodName: 'ac_' + eventData?.goodsName,
+ };
+ })
+ .transform('registry', (ctx, eventType, eventData) => {
+ return { ...eventData, userName: 'rg_' + eventData?.userName };
+ })
+ .after((ctx, eventType, reportData) => {
+ console.log('after', ctx, eventType, reportData);
+ })
+ .build();
+
+ const trackBuilder = createTrackBuilder<
+ TrackContext,
+ ReportEventData
+ >();
+
+ return trackBuilder
+ .init(() => {
+ return {
+ reportAdapter: adapter,
+ };
+ })
+ .before((ctx) => {
+ console.log('before track', ctx);
+ })
+ .after((ctx) => {
+ console.log('after track', ctx);
+ });
+};
+```
+
+
+
+
+```typescript title="reportAdapter.ts"
+export class ReportAdapter extends BaseAdapter<
+ TrackContext,
+ ReportEventData,
+ ReportAdapterOptions, ReportEventData>
+> {
+ isTrackable(
+ ctx: TrackContext,
+ eventType: EventType,
+ eventData: ReportEventData[EventType]
+ ): boolean | Promise {
+ return true;
+ }
+
+ protected report(
+ ctx: TrackContext,
+ reportData: AdapterReportData,
+ setupData?:
+ | { name: 'setup' | 'setup2' | 'setup3'; timeStamp: number }
+ | undefined
+ ): void | Promise {
+ console.log('report', ctx, reportData, setupData);
+ }
+}
+```
+
+
+
+
+
+```typescript title="types.ts"
+export type ReportAdapterOptions = {
+ setup?: (
+ ctx: Context,
+ eventTYpe: EventType,
+ eventData: EventData[EventType]
+ ) => Promise<{
+ name: 'setup' | 'setup2' | 'setup3';
+ timeStamp: number;
+ }>;
+};
+
+export type ReportTrackData = {
+ bizMode: 'test' | 'test2';
+ env: 'prod' | 'uat';
+ platform: 'android' | 'ios';
+ ip: string;
+ userId: string;
+};
+
+export type ReportEventData = {
+ registry?: {
+ userName: string;
+ mobile: string;
+ pwd: string;
+ email: string;
+ };
+ addCart?: {
+ price: number;
+ goodsId: string;
+ goodsName: string;
+ count: number;
+ };
+};
+```
+
+
+
+
+
+```typescript title="index.tsx"
+export const Index = () => {
+ const onAddToCart = async () => {
+ await reportTrack().select('reportAdapter').track('addCart', {
+ price: 25.99,
+ goodsId: '23432252',
+ goodsName: 'Long Chair',
+ count: 1,
+ });
+ };
+ return (
+
+
+
+
+
+
+
Long Chair
+
ID: 23432252
+
+
$25.99
+
+
+ Add to cart
+
+
+
+
+ );
+};
+```
+
+
+
+
+## Congratulations !
+
+That's all, now let's deep dive into the [props](/docs/api/base-adapter).
diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts
index 1a2482b..7d02e37 100644
--- a/website/docusaurus.config.ts
+++ b/website/docusaurus.config.ts
@@ -11,7 +11,7 @@ const config: Config = {
baseUrl: '/track',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
- favicon: '/icon/favicon.ico',
+ favicon: '/icon/favicon.svg',
organizationName: 'Hyperse',
projectName: 'hyperse tracker',
presets: [
@@ -47,7 +47,8 @@ const config: Config = {
},
docs: {
sidebar: {
- hideable: true,
+ hideable: false,
+ autoCollapseCategories: true,
},
},
navbar: {
@@ -63,9 +64,14 @@ const config: Config = {
items: [
{
type: 'doc',
- docId: 'intro',
+ docId: 'intro/introducing',
position: 'left',
- label: 'Documentation',
+ label: 'User Guide',
+ },
+ {
+ href: 'https://www.npmjs.com/package/@hyperse/track',
+ label: 'NPM',
+ position: 'right',
},
{
href: 'https://github.com/hyperse-io/track',
@@ -73,23 +79,49 @@ const config: Config = {
position: 'right',
},
{
- href: 'https://www.npmjs.com/package/@hyperse/track',
- label: 'NPM',
+ href: 'https://discord.com/invite/tj3ahjXXzM',
+ label: 'Discord',
position: 'right',
},
],
},
footer: {
- style: 'dark',
- logo: {
- alt: 'Hyperse',
- src: '/img/logo.svg',
- height: 40,
- style: {
- borderRadius: '2px',
+ links: [
+ {
+ title: 'Resources',
+ items: [
+ {
+ label: 'hyperse',
+ href: 'https://www.hyperse.net/',
+ },
+ {
+ label: 'hyperse blog',
+ href: 'https://www.hyperse.net/blog',
+ },
+ {
+ label: 'hyperse devutils',
+ href: 'https://devutils.hyperse.net/',
+ },
+ ],
},
- href: 'https://github.com/hyperse-io',
- },
+ {
+ title: 'Community',
+ items: [
+ {
+ label: 'Discord',
+ href: 'https://discord.com/invite/tj3ahjXXzM',
+ },
+ {
+ label: 'Twitter',
+ href: 'https://x.com/hyperse_net',
+ },
+ {
+ label: 'GitHub',
+ href: 'https://github.com/hyperse-io',
+ },
+ ],
+ },
+ ],
copyright: `Copyright © ${new Date().getFullYear()} Hyperse`,
},
algolia: {
diff --git a/website/package.json b/website/package.json
index 4799c42..6bffb9a 100644
--- a/website/package.json
+++ b/website/package.json
@@ -53,6 +53,7 @@
"@heroicons/react": "^2.1.5",
"@mdx-js/react": "^3.0.1",
"clsx": "^2.1.1",
+ "docusaurus-theme-search-typesense": "^0.20.0",
"postcss": "^8.4.40",
"prism-react-renderer": "^2.3.1",
"react": "^18.3.1",
diff --git a/website/sidebars.ts b/website/sidebars.ts
index 1f5a17b..c97b3b8 100644
--- a/website/sidebars.ts
+++ b/website/sidebars.ts
@@ -14,13 +14,34 @@ const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [
{
- type: 'doc',
- id: 'intro',
+ type: 'category',
label: 'Getting Started',
+ items: [
+ {
+ type: 'doc',
+ id: 'intro/introducing',
+ label: 'Introducing',
+ },
+ {
+ type: 'doc',
+ id: 'intro/installation',
+ label: 'Installation',
+ },
+ {
+ type: 'doc',
+ id: 'intro/sample-example',
+ label: 'Sample Example',
+ },
+ {
+ type: 'doc',
+ id: 'intro/live-example',
+ label: 'Live Example',
+ },
+ ],
},
{
type: 'category',
- label: 'API',
+ label: 'How-to Guides',
items: [
{
type: 'doc',
@@ -41,7 +62,7 @@ const sidebars: SidebarsConfig = {
},
{
type: 'category',
- label: 'Adapters',
+ label: 'Core Plugins',
items: [
{
type: 'doc',
diff --git a/website/src/components/Amimate/DynamicCoding.tsx b/website/src/components/Amimate/DynamicCoding.tsx
index d08f40d..2e5a869 100644
--- a/website/src/components/Amimate/DynamicCoding.tsx
+++ b/website/src/components/Amimate/DynamicCoding.tsx
@@ -1,8 +1,8 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect } from 'react';
+import clsx from 'clsx';
+import styles from './styles.module.css';
export default function DynamicCoding(): JSX.Element {
- const preRef = useRef(null);
-
const [mounted, setMounted] = React.useState(false);
useEffect(() => {
@@ -16,19 +16,22 @@ export default function DynamicCoding(): JSX.Element {
}, [mounted]);
return (
-
+
+
+
+
+
);
}
diff --git a/website/src/components/Amimate/styles.module.css b/website/src/components/Amimate/styles.module.css
new file mode 100644
index 0000000..1bb37f6
--- /dev/null
+++ b/website/src/components/Amimate/styles.module.css
@@ -0,0 +1,4 @@
+.code_video {
+ box-shadow: rgba(239, 240, 241, 0.102) 0px 6px 43px;
+ border-radius: 1rem;
+}
diff --git a/website/src/components/HomepageFeatures/index.tsx b/website/src/components/HomepageFeatures/index.tsx
index a56f21f..025405b 100644
--- a/website/src/components/HomepageFeatures/index.tsx
+++ b/website/src/components/HomepageFeatures/index.tsx
@@ -1,38 +1,124 @@
import React from 'react';
-import clsx from 'clsx';
-import mountain from '@site/static/img/undraw_docusaurus_mountain.svg';
-import react from '@site/static/img/undraw_docusaurus_react.svg';
-import tree from '@site/static/img/undraw_docusaurus_tree.svg';
+import bars from '@site/static/img/bars.svg';
+import browser from '@site/static/img/browser.svg';
+import lightbulb from '@site/static/img/lightbulb.svg';
+import muscle from '@site/static/img/muscle.svg';
+import puzzle from '@site/static/img/puzzle.svg';
+import shield from '@site/static/img/shield.svg';
import styles from './styles.module.css';
const FeatureList = [
{
- title: 'Easy to Use',
- Svg: mountain,
+ title: 'Typed',
+ Svg: shield,
description: (
<>
- Docusaurus was designed from the ground up to be easily installed and
- used to get your website up and running quickly.
+
+ Leveraging TypeScript's static type checking, Hyperse ensures that
+ most errors are caught during development, reducing runtime issues and
+ enhancing the robustness and stability of front-end code.
+
+
+ The strong typing system also aids developers in faster code
+ completion, refactoring, and debugging, thereby increasing development
+ efficiency.
+
>
),
},
{
- title: 'Focus on What Matters',
- Svg: tree,
+ title: 'Smart',
+ Svg: lightbulb,
description: (
<>
- Docusaurus lets you focus on your docs, and we'll do the chores. Go
- ahead and move your docs into the docs
directory.
+
+ Hyperse integrates intelligent data collection strategies that
+ automatically optimize the collection process based on user behavior,
+ data changes, and network conditions, ensuring real-time data accuracy
+ and timeliness.
+
+
+ It supports dynamic configuration, allowing the adjustment of
+ collection strategies to meet performance needs across different
+ front-end scenarios.
+
>
),
},
{
- title: 'Powered by React',
- Svg: react,
+ title: 'Scalable',
+ Svg: bars,
description: (
<>
- Extend or customize your website layout by reusing React. Docusaurus can
- be extended while reusing the same header and footer.
+
+ Optimized for large-scale front-end data collection tasks, Hyperse
+ efficiently gathers necessary data whether in single-page applications
+ (SPA) or multi-page applications.
+
+
+ Its modular design allows developers to extend and adjust collection
+ features as needed, ensuring scalability to accommodate future
+ requirements.
+
+ >
+ ),
+ },
+
+ {
+ title: 'Powerful',
+ Svg: muscle,
+ description: (
+ <>
+
+ Supports multiple data sources and formats, including browser APIs,
+ user inputs, DOM elements, and network requests, covering a wide range
+ of front-end data collection scenarios.
+
+
+ Offers flexible configuration options, enabling developers to
+ customize collection frequency, data formats, filtering rules, and
+ more, catering to specific application needs.
+
+
+ Integrated with basic data cleansing functionalities, ensuring the
+ collected data is accurate and consistent.
+
+ >
+ ),
+ },
+
+ {
+ title: 'Front-End Friendly',
+ Svg: browser,
+ description: (
+ <>
+
+ Designed with front-end performance and user experience in mind,
+ Hyperse collects data without significantly impacting page load times
+ or user interactions.
+
+
+ Its lightweight design ensures smooth operation even on
+ resource-constrained mobile devices.
+
+ >
+ ),
+ },
+
+ {
+ title: 'Easy Integration',
+ Svg: puzzle,
+ description: (
+ <>
+
+ Provides an intuitive API that simplifies integration with existing
+ front-end frameworks and libraries such as React, Vue, and Angular.
+
+
+ Supports real-time data transmission to backend systems or third-party
+ services via Webhooks or other front-end communication methods,
+ ensuring a smooth data flow.
+
>
),
},
@@ -40,28 +126,35 @@ const FeatureList = [
function Feature({ Svg, title, description }) {
return (
-
-
-
-
-
-
{title}
-
{description}
+
+
-
+
+
+
+
);
}
export default function HomepageFeatures() {
return (
-
-
-
+
+
+
{FeatureList.map((props, idx) => (
))}
-
-
+
+
);
}
diff --git a/website/src/components/PreviewUML/index.tsx b/website/src/components/PreviewUML/index.tsx
new file mode 100644
index 0000000..e3b1fda
--- /dev/null
+++ b/website/src/components/PreviewUML/index.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import UmlImageUrlDark from '@site/static/img/uml-dark.png';
+import UmlImageUrlLight from '@site/static/img/uml-light.png';
+
+export default function Index() {
+ return (
+
+
+
+
+ );
+}
diff --git a/website/src/components/Stackblitz/index.tsx b/website/src/components/Stackblitz/index.tsx
index 943db85..de0d253 100644
--- a/website/src/components/Stackblitz/index.tsx
+++ b/website/src/components/Stackblitz/index.tsx
@@ -8,7 +8,7 @@ export default function Stackblitz(props: { id: string }) {
minHeight: '500px',
borderRadius: '8px',
}}
- src={`https://stackblitz.com/edit/${props.id}?ctl=1&embed=1`}
+ src={`https://stackblitz.com/edit/${props.id}?embed=1&theme=dark`}
/>
);
}
diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css
index 98cf351..68b03f3 100644
--- a/website/src/pages/index.module.css
+++ b/website/src/pages/index.module.css
@@ -16,8 +16,116 @@
}
.topBackground {
- background: linear-gradient(180deg,hsla(0,0%,100%,.031) 0,rgba(102,117,127,.102));
- clip-path: polygon(100% 0,0 0,0 65%,1% 64.95%,2% 64.8%,3% 64.6%,4% 64.3%,5% 63.9%,6% 63.45%,7% 62.9%,8% 62.25%,9% 61.55%,10% 60.8%,11% 59.95%,12% 59.05%,13% 58.1%,14% 57.1%,15% 56.05%,16% 55%,17% 53.9%,18% 52.8%,19% 51.65%,20% 50.5%,21% 49.35%,22% 48.2%,23% 47.05%,24% 45.9%,25% 44.8%,26% 43.75%,27% 42.75%,28% 41.75%,29% 40.8%,30% 39.9%,31% 39.1%,32% 38.35%,33% 37.65%,34% 37.05%,35% 36.5%,36% 36.05%,37% 35.65%,38% 35.35%,39% 35.15%,40% 35.05%,41% 35%,42% 35.05%,43% 35.2%,44% 35.45%,45% 35.75%,46% 36.15%,47% 36.65%,48% 37.2%,49% 37.85%,50% 38.55%,51% 39.35%,52% 40.2%,53% 41.1%,54% 42.05%,55% 43.05%,56% 44.1%,57% 45.15%,58% 46.3%,59% 47.4%,60% 48.55%,61% 49.7%,62% 50.85%,63% 52%,64% 53.15%,65% 54.25%,66% 55.35%,67% 56.4%,68% 57.45%,69% 58.4%,70% 59.35%,71% 60.2%,72% 61.05%,73% 61.8%,74% 62.45%,75% 63.05%,76% 63.6%,77% 64.05%,78% 64.4%,79% 64.7%,80% 64.85%,81% 65%,82% 65%,83% 64.9%,84% 64.75%,85% 64.5%,86% 64.2%,87% 63.75%,88% 63.25%,89% 62.7%,90% 62.05%,91% 61.3%,92% 60.5%,93% 59.65%,94% 58.75%,95% 57.8%,96% 56.8%,97% 55.75%,98% 54.65%,99% 53.55%,100% 52.4%);
+ background: linear-gradient(
+ 180deg,
+ hsla(0, 0%, 100%, 0.031) 0,
+ rgba(102, 117, 127, 0.102)
+ );
+ clip-path: polygon(
+ 100% 0,
+ 0 0,
+ 0 65%,
+ 1% 64.95%,
+ 2% 64.8%,
+ 3% 64.6%,
+ 4% 64.3%,
+ 5% 63.9%,
+ 6% 63.45%,
+ 7% 62.9%,
+ 8% 62.25%,
+ 9% 61.55%,
+ 10% 60.8%,
+ 11% 59.95%,
+ 12% 59.05%,
+ 13% 58.1%,
+ 14% 57.1%,
+ 15% 56.05%,
+ 16% 55%,
+ 17% 53.9%,
+ 18% 52.8%,
+ 19% 51.65%,
+ 20% 50.5%,
+ 21% 49.35%,
+ 22% 48.2%,
+ 23% 47.05%,
+ 24% 45.9%,
+ 25% 44.8%,
+ 26% 43.75%,
+ 27% 42.75%,
+ 28% 41.75%,
+ 29% 40.8%,
+ 30% 39.9%,
+ 31% 39.1%,
+ 32% 38.35%,
+ 33% 37.65%,
+ 34% 37.05%,
+ 35% 36.5%,
+ 36% 36.05%,
+ 37% 35.65%,
+ 38% 35.35%,
+ 39% 35.15%,
+ 40% 35.05%,
+ 41% 35%,
+ 42% 35.05%,
+ 43% 35.2%,
+ 44% 35.45%,
+ 45% 35.75%,
+ 46% 36.15%,
+ 47% 36.65%,
+ 48% 37.2%,
+ 49% 37.85%,
+ 50% 38.55%,
+ 51% 39.35%,
+ 52% 40.2%,
+ 53% 41.1%,
+ 54% 42.05%,
+ 55% 43.05%,
+ 56% 44.1%,
+ 57% 45.15%,
+ 58% 46.3%,
+ 59% 47.4%,
+ 60% 48.55%,
+ 61% 49.7%,
+ 62% 50.85%,
+ 63% 52%,
+ 64% 53.15%,
+ 65% 54.25%,
+ 66% 55.35%,
+ 67% 56.4%,
+ 68% 57.45%,
+ 69% 58.4%,
+ 70% 59.35%,
+ 71% 60.2%,
+ 72% 61.05%,
+ 73% 61.8%,
+ 74% 62.45%,
+ 75% 63.05%,
+ 76% 63.6%,
+ 77% 64.05%,
+ 78% 64.4%,
+ 79% 64.7%,
+ 80% 64.85%,
+ 81% 65%,
+ 82% 65%,
+ 83% 64.9%,
+ 84% 64.75%,
+ 85% 64.5%,
+ 86% 64.2%,
+ 87% 63.75%,
+ 88% 63.25%,
+ 89% 62.7%,
+ 90% 62.05%,
+ 91% 61.3%,
+ 92% 60.5%,
+ 93% 59.65%,
+ 94% 58.75%,
+ 95% 57.8%,
+ 96% 56.8%,
+ 97% 55.75%,
+ 98% 54.65%,
+ 99% 53.55%,
+ 100% 52.4%
+ );
left: 0;
position: absolute;
top: 0rem;
@@ -27,4 +135,4 @@
::-webkit-scrollbar {
display: none;
-}
\ No newline at end of file
+}
diff --git a/website/src/pages/index.tsx b/website/src/pages/index.tsx
index a7467e5..856ccaa 100644
--- a/website/src/pages/index.tsx
+++ b/website/src/pages/index.tsx
@@ -5,6 +5,7 @@ import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { CheckIcon } from '@heroicons/react/24/outline';
import { DocumentDuplicateIcon } from '@heroicons/react/24/outline';
+import DynamicCoding from '@site/src/components/Amimate/DynamicCoding';
import HomepageFeatures from '@site/src/components/HomepageFeatures';
import LogoDark from '@site/static/img/logo-dark.svg';
import LogoWhite from '@site/static/img/logo-white.svg';
@@ -34,24 +35,24 @@ function HomepageHeader() {
Watch
Get Started
Donate
-
+
-
-
+
+
+
+
diff --git a/website/static/img/bars.svg b/website/static/img/bars.svg
new file mode 100644
index 0000000..f2b50be
--- /dev/null
+++ b/website/static/img/bars.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/static/img/browser.svg b/website/static/img/browser.svg
new file mode 100644
index 0000000..5e3ad67
--- /dev/null
+++ b/website/static/img/browser.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/static/img/lightbulb.svg b/website/static/img/lightbulb.svg
new file mode 100644
index 0000000..65607ba
--- /dev/null
+++ b/website/static/img/lightbulb.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/website/static/img/logo.svg b/website/static/img/logo.svg
index 3d41926..7ea2d3d 100644
--- a/website/static/img/logo.svg
+++ b/website/static/img/logo.svg
@@ -1,199 +1,13 @@
-
-
-
-
-
-
-
-
+
+
+
+
+ Artboard 4
+
+
+
+
diff --git a/website/static/img/muscle.svg b/website/static/img/muscle.svg
new file mode 100644
index 0000000..f2b50be
--- /dev/null
+++ b/website/static/img/muscle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/static/img/puzzle.svg b/website/static/img/puzzle.svg
new file mode 100644
index 0000000..fe37abf
--- /dev/null
+++ b/website/static/img/puzzle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/website/static/img/shield.svg b/website/static/img/shield.svg
new file mode 100644
index 0000000..e40cdd3
--- /dev/null
+++ b/website/static/img/shield.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/website/static/img/uml-dark.png b/website/static/img/uml-dark.png
new file mode 100644
index 0000000000000000000000000000000000000000..ea9a7d1e0447a0fb5a8f231f99bbf142c2e30028
GIT binary patch
literal 124573
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