@codelockpro/sdk 0.0.1 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ CodeLockPro SDK License
2
+
3
+ Copyright (c) CodeLockPro. All rights reserved.
4
+
5
+ This software (the "SDK") is proprietary to CodeLockPro and is licensed,
6
+ not sold. Use of the SDK is permitted only in combination with an active
7
+ CodeLockPro account in good standing.
8
+
9
+ Subject to that condition, you are granted a non-exclusive,
10
+ non-transferable, non-sublicensable, revocable license to install and
11
+ use the SDK, and to integrate it into your own applications, solely for
12
+ the purpose of consuming the CodeLockPro service authorised by your
13
+ account.
14
+
15
+ You may not:
16
+
17
+ 1. use the SDK without an active CodeLockPro account;
18
+ 2. use the SDK to access any service other than CodeLockPro;
19
+ 3. sublicense, sell, rent, lease, or otherwise commercially
20
+ redistribute the SDK as a stand-alone product;
21
+ 4. remove or alter any copyright, trademark, or other proprietary
22
+ notices contained in the SDK.
23
+
24
+ The SDK is provided "AS IS", without warranty of any kind, express or
25
+ implied, including but not limited to the warranties of merchantability,
26
+ fitness for a particular purpose, and non-infringement. In no event
27
+ shall CodeLockPro be liable for any claim, damages, or other liability
28
+ arising from, out of, or in connection with the SDK or the use or other
29
+ dealings in the SDK.
30
+
31
+ This license terminates automatically if your CodeLockPro account is
32
+ terminated, suspended, or otherwise becomes inactive. On termination
33
+ you must cease all use of the SDK and remove all copies under your
34
+ control.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # `@codelockpro/sdk`
2
+
3
+ Plain-vanilla, modular **client** SDK — the CodeLockPro client
4
+ framework. Ships with the knowledge base as a built-in module;
5
+ additional modules plug into the same `CodeLockPro` instance through
6
+ the same registration surface.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ npm install @codelockpro/sdk
12
+ ```
13
+
14
+ > **License.** The SDK is proprietary and may only be used in
15
+ > combination with an active CodeLockPro account. See
16
+ > [`LICENSE`](./LICENSE).
17
+
18
+ ## Architecture invariants
19
+
20
+ 1. **Modular foundation.** The core has no per-module coupling. KB is
21
+ just the first module registered.
22
+ 2. **Server-mediated only.** The SDK is configured with the developer's
23
+ own base URL. **It must never talk directly to the CodeLockPro
24
+ API.** The developer's server is the authoritative middle layer.
25
+ 3. **Plain vanilla.** No framework dependency. Hosts (Vue, React,
26
+ Next.js, plain DOM) bind their own state on top of the SDK's data,
27
+ events, and actions.
28
+
29
+ ## Usage
30
+
31
+ ```ts
32
+ import { CodeLockPro } from "@codelockpro/sdk";
33
+
34
+ const client = new CodeLockPro({ baseUrl: "https://example.com/my-api" });
35
+
36
+ // Data
37
+ const { articles } = await client.kb.getArticles();
38
+ const article = await client.kb.getArticle("how-to-reset-password");
39
+
40
+ // Actions
41
+ await client.kb.trackView(article.id, { slug: article.slug });
42
+
43
+ // Events (subscribe once, in any framework)
44
+ client.on("kb.article.viewed", ({ articleId }) => {
45
+ console.log("viewed", articleId);
46
+ });
47
+ client.on("kb.search.performed", ({ query, count }) => {
48
+ console.log(`${count} results for ${query}`);
49
+ });
50
+ ```
51
+
52
+ `client.kb` is a convenience accessor for
53
+ `client.module<KnowledgeBaseModule>("kb")`. The core has no
54
+ KB-specific code path.
55
+
56
+ ## Registering more modules
57
+
58
+ ```ts
59
+ import { CodeLockPro, ModuleContext } from "@codelockpro/sdk";
60
+
61
+ function createCheckoutModule(ctx: ModuleContext) {
62
+ return {
63
+ async start(productId: string) {
64
+ const session = await ctx.request<{ url: string }>(
65
+ "/checkout/sessions",
66
+ { method: "POST", body: JSON.stringify({ productId }) },
67
+ );
68
+ ctx.bus.emit(`${ctx.name}.session.created`, { productId });
69
+ return session;
70
+ },
71
+ };
72
+ }
73
+
74
+ const client = new CodeLockPro({
75
+ baseUrl: "https://example.com/my-api",
76
+ modules: [
77
+ ["kb", (ctx) => /* … */], // optional — opts out of the default
78
+ ["checkout", createCheckoutModule],
79
+ ],
80
+ });
81
+
82
+ // or after construction:
83
+ client.register("chatbot", createChatbotModule);
84
+
85
+ const checkout = client.module<{ start: (id: string) => Promise<{ url: string }> }>("checkout");
86
+ await checkout.start("pro-monthly");
87
+ ```
88
+
89
+ ## Module author contract
90
+
91
+ A module factory is `(ctx: ModuleContext) => MyModule`. The context
92
+ provides:
93
+
94
+ - `ctx.name` — the registered name (`"kb"`, `"checkout"`, …)
95
+ - `ctx.request` — HTTP helper bound to the developer's `baseUrl`
96
+ - `ctx.bus` — shared event bus (`on` / `off` / `emit`)
97
+
98
+ Modules namespace their events as `<ctx.name>.<event>` so listeners stay
99
+ unambiguous when many modules are registered.
100
+
101
+ ## Expected server-side proxy (KB)
102
+
103
+ By default the KB module hits `${baseUrl}/kb/articles`,
104
+ `${baseUrl}/kb/articles/{idOrSlug}`, `${baseUrl}/kb/categories`,
105
+ `${baseUrl}/kb/search`, and `POST ${baseUrl}/kb/articles/{id}/views`.
106
+ The developer's server is expected to forward those to the upstream
107
+ CodeLockPro public read API (the PHP companion package
108
+ `codelockpro/sdk` ships ready-made helpers for that step).
109
+
110
+ ## Releasing
111
+
112
+ Releases are lock-stepped with the PHP package `codelockpro/sdk` —
113
+ both packages always ship at the same version, and `sdk/js/package.json`
114
+ is the single source of truth for the version number.
115
+
116
+ To cut a release:
117
+
118
+ 1. Trigger the **SDK Release** workflow
119
+ (`.github/workflows/sdk-release.yml`) via "Run workflow".
120
+ 2. Pick `release_type`: `patch`, `minor`, or `major`.
121
+ 3. The workflow bumps `sdk/js/package.json`, commits, tags
122
+ `sdk-v<MAJOR.MINOR.PATCH>`, publishes `@codelockpro/sdk` to npm
123
+ (OIDC trusted publisher with `--provenance`), and pushes the
124
+ `sdk/php/` subtree to the `codelockpro-sdk-php` mirror that
125
+ Packagist watches.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Tiny synchronous event bus shared by all SDK modules.
3
+ *
4
+ * The bus is intentionally minimal — no priority, no async fan-out, no
5
+ * wildcard subscriptions — because the SDK is a thin data/action layer
6
+ * that the host app (Vue, React, Next.js, plain DOM) decorates with its
7
+ * own state-management primitives. Modules emit semantic namespaced
8
+ * events (e.g. ``kb.article.viewed``) and host code subscribes via
9
+ * :meth:`CodeLockPro.on`.
10
+ *
11
+ * The bus has no awareness of which module emits what — it is a
12
+ * module-agnostic primitive owned by the core.
13
+ */
14
+ export type EventHandler<T = unknown> = (payload: T) => void;
15
+ export interface EventBus {
16
+ on<T = unknown>(event: string, handler: EventHandler<T>): () => void;
17
+ off<T = unknown>(event: string, handler: EventHandler<T>): void;
18
+ emit<T = unknown>(event: string, payload?: T): void;
19
+ }
20
+ export declare function createEventBus(): EventBus;
21
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;AAE7D,MAAM,WAAW,QAAQ;IACxB,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IACrE,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChE,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;CACpD;AAED,wBAAgB,cAAc,IAAI,QAAQ,CA2CzC"}
@@ -0,0 +1,45 @@
1
+ export function createEventBus() {
2
+ const handlers = new Map();
3
+ function on(event, handler) {
4
+ if (typeof event !== "string" || event.length === 0) {
5
+ throw new Error("CodeLockPro.on: event name is required");
6
+ }
7
+ if (typeof handler !== "function") {
8
+ throw new Error("CodeLockPro.on: handler must be a function");
9
+ }
10
+ let set = handlers.get(event);
11
+ if (!set) {
12
+ set = new Set();
13
+ handlers.set(event, set);
14
+ }
15
+ set.add(handler);
16
+ return () => off(event, handler);
17
+ }
18
+ function off(event, handler) {
19
+ const set = handlers.get(event);
20
+ if (!set)
21
+ return;
22
+ set.delete(handler);
23
+ if (set.size === 0)
24
+ handlers.delete(event);
25
+ }
26
+ function emit(event, payload) {
27
+ const set = handlers.get(event);
28
+ if (!set || set.size === 0)
29
+ return;
30
+ // Snapshot so handlers may unsubscribe themselves safely.
31
+ for (const h of Array.from(set)) {
32
+ try {
33
+ h(payload);
34
+ }
35
+ catch (err) {
36
+ // Never let one handler break another. Surface to the
37
+ // host via ``console.error`` rather than swallowing.
38
+ // eslint-disable-next-line no-console
39
+ console.error(`CodeLockPro: handler for "${event}" threw`, err);
40
+ }
41
+ }
42
+ }
43
+ return { on, off, emit };
44
+ }
45
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,cAAc;IAC7B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;IAEtD,SAAS,EAAE,CAAI,KAAa,EAAE,OAAwB;QACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;YACV,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,OAAuB,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,SAAS,GAAG,CAAI,KAAa,EAAE,OAAwB;QACtD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,GAAG,CAAC,MAAM,CAAC,OAAuB,CAAC,CAAC;QACpC,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;YAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,SAAS,IAAI,CAAI,KAAa,EAAE,OAAW;QAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACnC,0DAA0D;QAC1D,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC;gBACJ,CAAC,CAAC,OAAO,CAAC,CAAC;YACZ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,sDAAsD;gBACtD,qDAAqD;gBACrD,sCAAsC;gBACtC,OAAO,CAAC,KAAK,CAAC,6BAA6B,KAAK,SAAS,EAAE,GAAG,CAAC,CAAC;YACjE,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { EventBus } from "./events.js";
2
+ /**
3
+ * Module contract.
4
+ *
5
+ * A module is anything the host code wants to attach to a
6
+ * :class:`CodeLockPro` instance. The core does not know about any
7
+ * specific module; modules are produced by user-supplied factories.
8
+ *
9
+ * A factory receives the {@link ModuleContext} and returns the
10
+ * module's public surface (an object). The shape is entirely up to the
11
+ * module author — by convention it exposes data accessors
12
+ * (``getArticles``), actions (``trackView``), and emits semantic
13
+ * events on the shared bus.
14
+ */
15
+ export interface ModuleContext {
16
+ /** Module name as registered (e.g. ``"kb"``). */
17
+ readonly name: string;
18
+ /** HTTP helper bound to the developer's own ``baseUrl``. */
19
+ request<T = unknown>(path: string, init?: RequestInit): Promise<T>;
20
+ /** Shared event bus. Modules namespace their events as ``"<name>.…"``. */
21
+ readonly bus: EventBus;
22
+ }
23
+ export type ModuleFactory<T = unknown> = (ctx: ModuleContext) => T;
24
+ /**
25
+ * Internal module registry. Exposed via :meth:`CodeLockPro.register` and
26
+ * :meth:`CodeLockPro.module`.
27
+ */
28
+ export declare class ModuleRegistry {
29
+ private readonly _modules;
30
+ register<T>(name: string, instance: T): T;
31
+ get<T>(name: string): T;
32
+ has(name: string): boolean;
33
+ names(): string[];
34
+ }
35
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/core/module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,aAAa;IAC7B,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnE,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,aAAa,KAAK,CAAC,CAAC;AAEnE;;;GAGG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IAEvD,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC;IAazC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;IAQvB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,KAAK,IAAI,MAAM,EAAE;CAGjB"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Internal module registry. Exposed via :meth:`CodeLockPro.register` and
3
+ * :meth:`CodeLockPro.module`.
4
+ */
5
+ export class ModuleRegistry {
6
+ constructor() {
7
+ this._modules = new Map();
8
+ }
9
+ register(name, instance) {
10
+ if (typeof name !== "string" || name.length === 0) {
11
+ throw new Error("CodeLockPro.register: module name is required");
12
+ }
13
+ if (this._modules.has(name)) {
14
+ throw new Error(`CodeLockPro.register: module "${name}" is already registered`);
15
+ }
16
+ this._modules.set(name, instance);
17
+ return instance;
18
+ }
19
+ get(name) {
20
+ const mod = this._modules.get(name);
21
+ if (mod === undefined) {
22
+ throw new Error(`CodeLockPro.module: no module registered as "${name}"`);
23
+ }
24
+ return mod;
25
+ }
26
+ has(name) {
27
+ return this._modules.has(name);
28
+ }
29
+ names() {
30
+ return Array.from(this._modules.keys());
31
+ }
32
+ }
33
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/core/module.ts"],"names":[],"mappings":"AA0BA;;;GAGG;AACH,MAAM,OAAO,cAAc;IAA3B;QACkB,aAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IA8BxD,CAAC;IA5BA,QAAQ,CAAI,IAAY,EAAE,QAAW;QACpC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACd,iCAAiC,IAAI,yBAAyB,CAC9D,CAAC;QACH,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAClC,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,GAAG,CAAI,IAAY;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,gDAAgD,IAAI,GAAG,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,GAAQ,CAAC;IACjB,CAAC;IAED,GAAG,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,KAAK;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;CACD"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `@codelockpro/sdk` — the CodeLockPro client framework.
3
+ *
4
+ * A generic, modular foundation developers use to build a complete
5
+ * user experience on their own site by composing CodeLockPro modules.
6
+ * The package ships with a single built-in module (``kb`` — knowledge
7
+ * base) and exposes the same registration surface for any additional
8
+ * modules the developer authors or that ship in future package
9
+ * versions.
10
+ *
11
+ * The core in this file has no knowledge of any specific module.
12
+ * Modules are attached at construction time (or later via
13
+ * :meth:`register`) and are looked up by name through :meth:`module`.
14
+ * Each module gets a shared {@link EventBus} and an HTTP helper bound
15
+ * to the developer's own backend URL.
16
+ *
17
+ * Architecture invariants (see /docs/sdk/README.md):
18
+ *
19
+ * 1. **Modular foundation.** No coupling to any specific module.
20
+ * 2. **Server-mediated access only.** The client SDK is configured
21
+ * with the developer's *own* base URL. It must never talk
22
+ * directly to the CodeLockPro API.
23
+ * 3. **Plain vanilla.** No framework binding. Hosts (Vue / React /
24
+ * Next.js / plain DOM) bind their own state on top of the SDK's
25
+ * data, events, and actions.
26
+ */
27
+ import { EventHandler } from "./core/events.js";
28
+ import { ModuleFactory } from "./core/module.js";
29
+ import { createKbModule, KnowledgeBaseModule } from "./modules/kb.js";
30
+ export interface CodeLockProConfig {
31
+ /**
32
+ * Base URL of the developer's own backend, e.g.
33
+ * ``https://example.com/my-api``. Module paths are appended to it
34
+ * (``/kb/articles`` etc.). Direct access to ``https://api.codelock.pro``
35
+ * is intentionally not supported — see invariant #2.
36
+ */
37
+ baseUrl: string;
38
+ /**
39
+ * Modules to register at construction time. Use ``false`` to skip the
40
+ * default registration (KB) and register everything explicitly via
41
+ * :meth:`register`.
42
+ *
43
+ * Each entry is a ``[name, factory]`` tuple — exactly the same shape
44
+ * the registry uses internally — so the same surface registers any
45
+ * future module the same way as KB:
46
+ *
47
+ * new CodeLockPro({
48
+ * baseUrl: "…",
49
+ * modules: [
50
+ * ["kb", createKbModule],
51
+ * ["checkout", createCheckoutModule],
52
+ * ],
53
+ * });
54
+ */
55
+ modules?: false | Array<[string, ModuleFactory<unknown>]>;
56
+ /** Optional fetch implementation override (tests / Node 16). */
57
+ fetch?: typeof fetch;
58
+ /** Optional default headers merged into every request. */
59
+ headers?: Record<string, string>;
60
+ }
61
+ export declare class CodeLockPro {
62
+ readonly baseUrl: string;
63
+ private readonly _fetch;
64
+ private readonly _headers;
65
+ private readonly _bus;
66
+ private readonly _registry;
67
+ constructor(config: CodeLockProConfig);
68
+ /**
69
+ * Register a module after construction. Modules can be defined by
70
+ * any caller — KB, future first-party modules, and even host-app
71
+ * extensions all use the same entry point.
72
+ */
73
+ register<T>(name: string, factory: ModuleFactory<T>): T;
74
+ /** Look up a previously registered module by name. */
75
+ module<T = unknown>(name: string): T;
76
+ /** Names of every currently-registered module. */
77
+ moduleNames(): string[];
78
+ /** Subscribe to an event on the shared bus. Returns an unsubscribe fn. */
79
+ on<T = unknown>(event: string, handler: EventHandler<T>): () => void;
80
+ off<T = unknown>(event: string, handler: EventHandler<T>): void;
81
+ emit<T = unknown>(event: string, payload?: T): void;
82
+ /**
83
+ * Convenience accessor for the knowledge-base module. Equivalent to
84
+ * ``client.module<KnowledgeBaseModule>("kb")``. Provided as ergonomic
85
+ * sugar so existing call sites read naturally; it is not part of the
86
+ * core's invariants — if KB is unregistered this throws.
87
+ */
88
+ get kb(): KnowledgeBaseModule;
89
+ /** Internal HTTP helper. Modules call this rather than fetch directly so
90
+ * cross-cutting concerns (auth, telemetry) can be added in one place. */
91
+ _request<T = unknown>(path: string, init?: RequestInit): Promise<T>;
92
+ }
93
+ export declare class CodeLockProApiError extends Error {
94
+ status: number;
95
+ body: string;
96
+ constructor(status: number, statusText: string, body: string);
97
+ }
98
+ export type { EventBus, EventHandler } from "./core/events.js";
99
+ export type { ModuleContext, ModuleFactory } from "./core/module.js";
100
+ export type { KnowledgeBaseModule, KbArticle, KbCategory, KbListOptions, } from "./modules/kb.js";
101
+ export { createKbModule };
102
+ export default CodeLockPro;
103
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAA4B,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAiB,aAAa,EAAkB,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IACjC;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1D,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,qBAAa,WAAW;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAW;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;gBAE/B,MAAM,EAAE,iBAAiB;IA2BrC;;;;OAIG;IACH,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC;IAUvD,sDAAsD;IACtD,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;IAIpC,kDAAkD;IAClD,WAAW,IAAI,MAAM,EAAE;IAIvB,0EAA0E;IAC1E,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIpE,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IAI/D,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI;IAInD;;;;;OAKG;IACH,IAAI,EAAE,IAAI,mBAAmB,CAE5B;IAED;6EACyE;IACnE,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;CAiB7E;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC1B,MAAM,EAAE,MAAM;IAA6B,IAAI,EAAE,MAAM;gBAAvD,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAS,IAAI,EAAE,MAAM;CAI1E;AAED,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,YAAY,EACX,mBAAmB,EACnB,SAAS,EACT,UAAU,EACV,aAAa,GACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,eAAe,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,126 @@
1
+ /**
2
+ * `@codelockpro/sdk` — the CodeLockPro client framework.
3
+ *
4
+ * A generic, modular foundation developers use to build a complete
5
+ * user experience on their own site by composing CodeLockPro modules.
6
+ * The package ships with a single built-in module (``kb`` — knowledge
7
+ * base) and exposes the same registration surface for any additional
8
+ * modules the developer authors or that ship in future package
9
+ * versions.
10
+ *
11
+ * The core in this file has no knowledge of any specific module.
12
+ * Modules are attached at construction time (or later via
13
+ * :meth:`register`) and are looked up by name through :meth:`module`.
14
+ * Each module gets a shared {@link EventBus} and an HTTP helper bound
15
+ * to the developer's own backend URL.
16
+ *
17
+ * Architecture invariants (see /docs/sdk/README.md):
18
+ *
19
+ * 1. **Modular foundation.** No coupling to any specific module.
20
+ * 2. **Server-mediated access only.** The client SDK is configured
21
+ * with the developer's *own* base URL. It must never talk
22
+ * directly to the CodeLockPro API.
23
+ * 3. **Plain vanilla.** No framework binding. Hosts (Vue / React /
24
+ * Next.js / plain DOM) bind their own state on top of the SDK's
25
+ * data, events, and actions.
26
+ */
27
+ import { createEventBus } from "./core/events.js";
28
+ import { ModuleRegistry } from "./core/module.js";
29
+ import { createKbModule } from "./modules/kb.js";
30
+ export class CodeLockPro {
31
+ constructor(config) {
32
+ if (!config || typeof config.baseUrl !== "string" || !config.baseUrl) {
33
+ throw new Error("CodeLockPro: baseUrl is required");
34
+ }
35
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
36
+ this._fetch = config.fetch ?? globalThis.fetch;
37
+ this._headers = config.headers ?? {};
38
+ if (typeof this._fetch !== "function") {
39
+ throw new Error("CodeLockPro: no fetch implementation available");
40
+ }
41
+ this._bus = createEventBus();
42
+ this._registry = new ModuleRegistry();
43
+ // Default registration: KB is module one. Pass ``modules: false``
44
+ // (or override with an explicit list) to opt out — the core
45
+ // itself has no knowledge-base coupling.
46
+ const defaultModules = [
47
+ ["kb", createKbModule],
48
+ ];
49
+ const toRegister = config.modules === false ? [] : config.modules ?? defaultModules;
50
+ for (const [name, factory] of toRegister) {
51
+ this.register(name, factory);
52
+ }
53
+ }
54
+ /**
55
+ * Register a module after construction. Modules can be defined by
56
+ * any caller — KB, future first-party modules, and even host-app
57
+ * extensions all use the same entry point.
58
+ */
59
+ register(name, factory) {
60
+ const ctx = {
61
+ name,
62
+ request: this._request.bind(this),
63
+ bus: this._bus,
64
+ };
65
+ const instance = factory(ctx);
66
+ return this._registry.register(name, instance);
67
+ }
68
+ /** Look up a previously registered module by name. */
69
+ module(name) {
70
+ return this._registry.get(name);
71
+ }
72
+ /** Names of every currently-registered module. */
73
+ moduleNames() {
74
+ return this._registry.names();
75
+ }
76
+ /** Subscribe to an event on the shared bus. Returns an unsubscribe fn. */
77
+ on(event, handler) {
78
+ return this._bus.on(event, handler);
79
+ }
80
+ off(event, handler) {
81
+ this._bus.off(event, handler);
82
+ }
83
+ emit(event, payload) {
84
+ this._bus.emit(event, payload);
85
+ }
86
+ /**
87
+ * Convenience accessor for the knowledge-base module. Equivalent to
88
+ * ``client.module<KnowledgeBaseModule>("kb")``. Provided as ergonomic
89
+ * sugar so existing call sites read naturally; it is not part of the
90
+ * core's invariants — if KB is unregistered this throws.
91
+ */
92
+ get kb() {
93
+ return this.module("kb");
94
+ }
95
+ /** Internal HTTP helper. Modules call this rather than fetch directly so
96
+ * cross-cutting concerns (auth, telemetry) can be added in one place. */
97
+ async _request(path, init = {}) {
98
+ const url = `${this.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
99
+ const res = await this._fetch(url, {
100
+ ...init,
101
+ headers: {
102
+ Accept: "application/json",
103
+ ...this._headers,
104
+ ...(init.headers || {}),
105
+ },
106
+ });
107
+ if (!res.ok) {
108
+ const text = await res.text().catch(() => "");
109
+ throw new CodeLockProApiError(res.status, res.statusText, text);
110
+ }
111
+ if (res.status === 204)
112
+ return undefined;
113
+ return (await res.json());
114
+ }
115
+ }
116
+ export class CodeLockProApiError extends Error {
117
+ constructor(status, statusText, body) {
118
+ super(`CodeLockPro API ${status} ${statusText}: ${body}`);
119
+ this.status = status;
120
+ this.body = body;
121
+ this.name = "CodeLockProApiError";
122
+ }
123
+ }
124
+ export { createKbModule };
125
+ export default CodeLockPro;
126
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Knowledge base — the built-in module of the CodeLockPro SDK.
3
+ *
4
+ * Wraps the developer's server-side proxy of the unauthenticated public
5
+ * KB endpoints. This module is **not** privileged inside the SDK core —
6
+ * it uses the same {@link ModuleFactory} contract any additional
7
+ * module the developer registers will use.
8
+ *
9
+ * Default expected paths under the configured ``baseUrl`` (override via
10
+ * ``options.basePath`` if your proxy mounts elsewhere):
11
+ *
12
+ * GET {basePath}/articles → list articles
13
+ * GET {basePath}/articles/{idOrSlug} → single article
14
+ * GET {basePath}/categories → list categories
15
+ * GET {basePath}/search?q=… → full-text search
16
+ * POST {basePath}/articles/{id}/track-view → record a view (action)
17
+ *
18
+ * Events emitted on the shared bus (subscribe via ``client.on(...)``):
19
+ *
20
+ * ``kb.article.viewed`` — payload ``{ articleId: string, slug?: string }``
21
+ * ``kb.search.performed`` — payload ``{ query: string, count: number }``
22
+ *
23
+ * Custom events from host apps may follow the same ``kb.*`` namespace.
24
+ */
25
+ import type { ModuleFactory } from "../core/module.js";
26
+ export interface KbArticle {
27
+ id: string;
28
+ application_id: string;
29
+ category_id: string | null;
30
+ developer_id: string;
31
+ slug: string;
32
+ title: string;
33
+ excerpt: string | null;
34
+ content?: string;
35
+ status: "draft" | "published";
36
+ published_at: string | null;
37
+ created_at: string | null;
38
+ updated_at: string | null;
39
+ }
40
+ export interface KbCategory {
41
+ id: string;
42
+ application_id: string;
43
+ developer_id: string;
44
+ name: string;
45
+ slug: string;
46
+ description: string | null;
47
+ created_at: string | null;
48
+ updated_at: string | null;
49
+ }
50
+ export interface KbListOptions {
51
+ categoryId?: string;
52
+ categorySlug?: string;
53
+ limit?: number;
54
+ startingAfter?: string;
55
+ }
56
+ export interface KbArticleViewedPayload {
57
+ articleId: string;
58
+ slug?: string;
59
+ }
60
+ export interface KbSearchPerformedPayload {
61
+ query: string;
62
+ count: number;
63
+ }
64
+ export interface KnowledgeBaseModule {
65
+ getArticles(opts?: KbListOptions): Promise<{
66
+ articles: KbArticle[];
67
+ }>;
68
+ getArticle(idOrSlug: string): Promise<KbArticle>;
69
+ getCategories(): Promise<{
70
+ categories: KbCategory[];
71
+ }>;
72
+ search(query: string, limit?: number): Promise<{
73
+ query: string;
74
+ articles: KbArticle[];
75
+ }>;
76
+ /**
77
+ * Record a view of an article via the developer's proxy and emit
78
+ * ``kb.article.viewed`` on the shared event bus. Errors from the
79
+ * server are swallowed so view-tracking never blocks rendering;
80
+ * the bus event always fires.
81
+ */
82
+ trackView(articleId: string, opts?: {
83
+ slug?: string;
84
+ }): Promise<void>;
85
+ /** Subscribe to an event scoped to this module (``kb.<event>``). */
86
+ on<T = unknown>(event: string, handler: (payload: T) => void): () => void;
87
+ off<T = unknown>(event: string, handler: (payload: T) => void): void;
88
+ }
89
+ export interface KbModuleOptions {
90
+ /** Override the proxy mount-point (default: ``/kb``). */
91
+ basePath?: string;
92
+ }
93
+ /**
94
+ * Module factory. Conforms to {@link ModuleFactory} — the SDK core treats
95
+ * KB exactly like any other module.
96
+ *
97
+ * const client = new CodeLockPro({
98
+ * baseUrl: "https://example.com/my-api",
99
+ * modules: [["kb", createKbModule]],
100
+ * });
101
+ */
102
+ export declare const createKbModule: ModuleFactory<KnowledgeBaseModule>;
103
+ /** Variant that accepts module-specific options (e.g. a custom basePath). */
104
+ export declare function createKbModuleWith(options: KbModuleOptions): ModuleFactory<KnowledgeBaseModule>;
105
+ //# sourceMappingURL=kb.d.ts.map