@stndrds/client 1.0.0-alpha.295

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,111 @@
1
+ STANDARDS SDK LICENSE
2
+ Version 1.0, September 2026
3
+
4
+ Copyright (c) 2025-2026 AND YOU CREATE SAS. All rights reserved.
5
+
6
+ AND YOU CREATE, a French société par actions simplifiée with a share capital
7
+ of EUR 500, registered with the Paris Trade and Companies Register under
8
+ number 940 190 614, having its registered office at 60 rue François Ier,
9
+ 75008 Paris, France (VAT FR34940190614) ("AYC", "we"), is the sole owner of
10
+ the software distributed under this license (the "SDK"): the source code,
11
+ compiled code, type declarations, documentation and any accompanying files
12
+ published in this package.
13
+
14
+ This is not an open source license. The SDK is made available so that you
15
+ can build software that communicates with the Standards platform operated by
16
+ AYC ("Standards"). Reading the source is permitted; everything else is
17
+ governed by the terms below. By installing, copying or using the SDK you
18
+ ("you", the "Licensee") accept them. If you do not accept them, do not use
19
+ the SDK.
20
+
21
+ 1. LICENSE GRANT
22
+ AYC grants you a limited, non-exclusive, non-transferable, non-sublicensable,
23
+ revocable license to:
24
+ (a) install and use the SDK, in unmodified form, solely to develop, test
25
+ and run applications that communicate with a Standards tenant, workspace
26
+ or API that you are authorised to access under a valid agreement with
27
+ AYC or with an AYC customer; and
28
+ (b) make the copies of the SDK reasonably necessary for that purpose,
29
+ including copies produced by your build tooling and bundlers.
30
+
31
+ 2. RESTRICTIONS
32
+ Except as expressly permitted in Section 1, you shall not, and shall not
33
+ permit any third party to:
34
+ (a) modify, translate, adapt or create derivative works of the SDK;
35
+ (b) reverse engineer, decompile or disassemble the SDK, or otherwise attempt
36
+ to derive its design or internal structure, except to the extent that
37
+ applicable law expressly permits it despite this restriction (Articles
38
+ L.122-6-1 III and IV of the French Intellectual Property Code), in which
39
+ case you shall first request the information from AYC;
40
+ (c) distribute, sell, rent, lease, lend, sublicense, publish or otherwise
41
+ make the SDK, or any part of it, available to any third party, other
42
+ than as bundled inside an application described in Section 1(a);
43
+ (d) use the SDK, or any knowledge of its interfaces, formats, protocols or
44
+ schemas obtained through it, to build, train or improve a product or
45
+ service that competes with Standards, or to access Standards other than
46
+ through the interfaces AYC documents;
47
+ (e) use the SDK to interact with any Standards instance, tenant or API that
48
+ you are not authorised to access, or to circumvent any authentication,
49
+ authorisation, rate limit, licence key or other technical measure;
50
+ (f) remove, alter or obscure any copyright, trademark, licence or other
51
+ proprietary notice in the SDK; or
52
+ (g) use AYC's or Standards' names, logos or trademarks except to state
53
+ truthfully that your application uses the Standards SDK.
54
+
55
+ 3. OWNERSHIP
56
+ The SDK is licensed, not sold. AYC and its licensors retain all right, title
57
+ and interest in and to the SDK, including all intellectual property rights.
58
+ No right is granted by implication, estoppel or otherwise. Any feedback or
59
+ suggestion you provide about the SDK may be used by AYC without restriction
60
+ or obligation.
61
+
62
+ 4. THIRD-PARTY COMPONENTS
63
+ The SDK may include or depend on third-party open source components. Those
64
+ components are governed by their own licences, which are identified in the
65
+ package metadata and prevail over this license for those components only.
66
+
67
+ 5. UPDATES
68
+ AYC may publish new versions of the SDK at any time. This license applies to
69
+ each version you obtain unless that version is accompanied by a different
70
+ licence. AYC has no obligation to maintain, support or update the SDK.
71
+
72
+ 6. TERM AND TERMINATION
73
+ This license is effective until terminated. It terminates automatically,
74
+ without notice, if you breach any of its terms. AYC may also terminate it on
75
+ written notice if your agreement with AYC or with the AYC customer whose
76
+ tenant you access ends. Upon termination you shall stop using the SDK and
77
+ destroy every copy in your possession or control. Sections 2, 3, 7, 8 and 9
78
+ survive termination.
79
+
80
+ 7. NO WARRANTY
81
+ THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE", WITHOUT WARRANTY OF ANY KIND,
82
+ EXPRESS OR IMPLIED, INCLUDING ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
83
+ PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, TO THE FULLEST EXTENT
84
+ PERMITTED BY APPLICABLE LAW. AYC does not warrant that the SDK will be
85
+ error-free or uninterrupted.
86
+
87
+ 8. LIMITATION OF LIABILITY
88
+ TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, AYC SHALL NOT BE LIABLE
89
+ FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR
90
+ FOR ANY LOSS OF PROFITS, REVENUE, DATA OR GOODWILL, ARISING OUT OF OR IN
91
+ CONNECTION WITH THE SDK OR THIS LICENSE. AYC'S TOTAL AGGREGATE LIABILITY
92
+ UNDER THIS LICENSE SHALL NOT EXCEED ONE HUNDRED EUROS (EUR 100). Nothing in
93
+ this license excludes or limits liability that cannot be excluded or limited
94
+ under applicable law, including liability for gross negligence, wilful
95
+ misconduct or death or personal injury caused by negligence.
96
+
97
+ 9. GOVERNING LAW AND JURISDICTION
98
+ This license is governed by the laws of France. Any dispute arising out of or
99
+ in connection with it shall be submitted to the exclusive jurisdiction of the
100
+ courts of Paris, France, subject to any mandatory rule granting jurisdiction
101
+ to another court.
102
+
103
+ 10. GENERAL
104
+ If any provision of this license is held unenforceable, the remaining
105
+ provisions remain in force. AYC's failure to enforce a provision is not a
106
+ waiver of it. This license is the entire agreement between you and AYC about
107
+ the SDK and supersedes any prior understanding about it, without prejudice to
108
+ any separate agreement governing your access to Standards.
109
+
110
+ To obtain a different licence, or for any question about this one, contact
111
+ AND YOU CREATE SAS, 60 rue François Ier, 75008 Paris, France.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @stndrds/client
2
+
3
+ Declare your schema with the `@stndrds/schema` fluent builders, sync it to a hosted Standards tenant, and operate typed records — from any Node ≥ 18 server process. No runtime, no NestJS.
4
+
5
+ ```bash
6
+ pnpm add @stndrds/client
7
+ ```
8
+
9
+ Everything you need — the builders, `createStandards`, `defineSchemaSource`, error classes — is exported from `@stndrds/client`. One package, one version.
10
+
11
+ ## 1. Declare a source
12
+
13
+ ```ts
14
+ import { date, defineSchemaSource, object, select, text } from "@stndrds/client";
15
+
16
+ export const incident = object({ name: "incidents", label: "Incident" })
17
+ .attribute(text({ name: "title", label: "Title" }).required())
18
+ .attribute(
19
+ select({ name: "severity", label: "Severity" }).options([
20
+ { value: "minor", label: "Minor" },
21
+ { value: "major", label: "Major" },
22
+ { value: "critical", label: "Critical" },
23
+ ])
24
+ )
25
+ .attribute(date({ name: "startedAt", label: "Started at" }))
26
+ .labelExpression("{{ title }}");
27
+
28
+ export const statusSource = defineSchemaSource("status-site", { objects: [incident] });
29
+ ```
30
+
31
+ ## 2. Sync it (server-side only)
32
+
33
+ ```ts
34
+ import { createStandards } from "@stndrds/client";
35
+
36
+ const standards = createStandards({
37
+ baseUrl: process.env.STANDARDS_API_URL!,
38
+ apiKey: process.env.STANDARDS_API_KEY!, // the key's role must grant architect:update (a key inherits its creator's roles unless created with a roleId)
39
+ });
40
+
41
+ const result = await standards.schema.sync(statusSource);
42
+ // { applied: false, hash } when nothing changed — one GET, no write.
43
+ ```
44
+
45
+ Objects you push are **system**: users can add their own attributes and views on top, but cannot delete or retype yours. Remove an object from the source and it is demoted to a custom object — its records survive. Never call `createStandards` with an API key in browser code; it refuses to.
46
+
47
+ ## 3. Typed records
48
+
49
+ ```ts
50
+ const incidents = standards.from(incident);
51
+
52
+ await incidents.create({ title: "API latency", severity: "major", startedAt: new Date() });
53
+ const critical = await incidents.eq("severity", "critical").orderBy("startedAt", "desc").limit(10).fetch();
54
+ ```
55
+
56
+ Inputs are typed from the builder (`severity` only accepts the three options); dates come back as `Date`.
57
+
58
+ ## Errors
59
+
60
+ `StandardsAuthError` (401/403), `SchemaSourceConflictError` (409 from `schema.sync`, with `objectName` and `owner`), `SchemaSourceValidationError` (400 from `schema.sync`, server message verbatim), `StandardsRequestError` (any other non-2xx, with the server's `code`), `StandardsConfigurationError` (misuse). `schema.sync` never throws for "nothing to do".
61
+
62
+ Licensed under the Standards SDK License 1.0 — see `LICENSE`. This package is not open source: it is provided to build applications that communicate with Standards.
@@ -0,0 +1,220 @@
1
+ import { ObjectDefinition, ViewDefinition, ExtractRecord, ExtractRecordInputStrict, ExtractRecordUpdateStrict } from '@stndrds/schema';
2
+ export { Attribute, CollectionPage, CollectionResponse, DuplicateError, ExtractRecord, ExtractRecordInput, ExtractRecordInputStrict, ExtractRecordStrict, ExtractRecordUpdate, ExtractRecordUpdateStrict, FilterCombinator, FilterOperator, FilterRule, FilterState, ForbiddenError, ListViewConfig, NotFoundError, ObjectDefinition, RecordMetadata, SCHEMA_SOURCE_ID_PATTERN, SchemaError, SchemaErrorCode, SortDirection, SortRule, ValidationError, ViewConfig, ViewDefinition, assertSchemaSourceId, canonicalStringify, checkbox, currency, date, detailView, document, formula, group, listView, location, multiselect, number, object, phone, relation, richtext, rollup, select, status, text, user } from '@stndrds/schema';
3
+
4
+ /**
5
+ * Anything carrying the fluent builder's phantom types and a `build()` — the
6
+ * `object()` builder. Typed structurally so a consumer never has to import the
7
+ * builder class to satisfy a signature.
8
+ */
9
+ interface ObjectBuilderLike {
10
+ readonly _types: {
11
+ record: unknown;
12
+ input: unknown;
13
+ update: unknown;
14
+ };
15
+ build(): ObjectDefinition;
16
+ }
17
+ type ViewInput = ViewDefinition | {
18
+ build(): ViewDefinition;
19
+ };
20
+ interface SchemaSource<TObjects extends readonly ObjectBuilderLike[] = readonly ObjectBuilderLike[]> {
21
+ readonly id: string;
22
+ /** sha256 hex of `canonicalStringify({ objects, views })` — the same digest the server recomputes. */
23
+ readonly hash: string;
24
+ readonly objects: ObjectDefinition[];
25
+ readonly views: ViewDefinition[];
26
+ /** The builders, kept for type inference in `standards.from(builder)`. */
27
+ readonly builders: TObjects;
28
+ }
29
+ /**
30
+ * Node's `node:crypto` is the only import in this package that is not a Web
31
+ * API; keeping it confined here leaves the rest of the client runtime-agnostic.
32
+ * Node >= 18 is the supported runtime for this slice; edge is a follow-up.
33
+ */
34
+ declare function hashSchemaSource(payload: {
35
+ objects: ObjectDefinition[];
36
+ views: ViewDefinition[];
37
+ }): string;
38
+ /**
39
+ * Declare the schema an external codebase owns on a tenant. Builders are built
40
+ * once here and the hash is computed once; the builders themselves are kept so
41
+ * `from()` can infer record types.
42
+ */
43
+ declare function defineSchemaSource<const TObjects extends readonly ObjectBuilderLike[]>(id: string, config: {
44
+ objects: TObjects;
45
+ views?: ViewInput[];
46
+ }): SchemaSource<TObjects>;
47
+
48
+ type Attr<TBuilder> = keyof ExtractRecord<TBuilder> & string;
49
+ /**
50
+ * The builder's own attributes keep their exact types (a `select` refuses an
51
+ * option it does not declare); runtime-added custom attributes stay writable
52
+ * through the open index. `ExtractRecordInput`'s own index is typed
53
+ * `CustomAttributeValue`, which has no `Date` member and would reject the very
54
+ * date values the REST API accepts — hence the strict variant plus this index.
55
+ */
56
+ type RecordInput<TBuilder> = ExtractRecordInputStrict<TBuilder> & {
57
+ [key: string]: unknown;
58
+ };
59
+ type RecordUpdate<TBuilder> = ExtractRecordUpdateStrict<TBuilder> & {
60
+ [key: string]: unknown;
61
+ };
62
+ /** The scalar shapes the fluent entry points accept; `FilterRule.value` is wider. */
63
+ type RuleValue = string | number | boolean | null;
64
+ interface FetchResult<TBuilder> {
65
+ records: ExtractRecord<TBuilder>[];
66
+ total: number | null;
67
+ }
68
+ interface RecordsQuery<TBuilder> {
69
+ eq(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
70
+ neq(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
71
+ gt(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
72
+ gte(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
73
+ lt(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
74
+ lte(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
75
+ contains(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
76
+ notContains(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
77
+ startsWith(attribute: Attr<TBuilder>, value: string): RecordsQuery<TBuilder>;
78
+ endsWith(attribute: Attr<TBuilder>, value: string): RecordsQuery<TBuilder>;
79
+ in(attribute: Attr<TBuilder>, values: string[]): RecordsQuery<TBuilder>;
80
+ notIn(attribute: Attr<TBuilder>, values: string[]): RecordsQuery<TBuilder>;
81
+ isEmpty(attribute: Attr<TBuilder>): RecordsQuery<TBuilder>;
82
+ isNotEmpty(attribute: Attr<TBuilder>): RecordsQuery<TBuilder>;
83
+ or(): RecordsQuery<TBuilder>;
84
+ orderBy(attribute: Attr<TBuilder>, direction?: "asc" | "desc"): RecordsQuery<TBuilder>;
85
+ limit(count: number): RecordsQuery<TBuilder>;
86
+ offset(count: number): RecordsQuery<TBuilder>;
87
+ search(query: string): RecordsQuery<TBuilder>;
88
+ fetch(): Promise<FetchResult<TBuilder>>;
89
+ single(): Promise<ExtractRecord<TBuilder> | null>;
90
+ get(id: string): Promise<ExtractRecord<TBuilder>>;
91
+ create(input: RecordInput<TBuilder>): Promise<ExtractRecord<TBuilder>>;
92
+ update(id: string, patch: RecordUpdate<TBuilder>): Promise<ExtractRecord<TBuilder>>;
93
+ delete(id: string): Promise<void>;
94
+ }
95
+ /**
96
+ * The builder shape `from(objectName: string)` stands in for: no declared
97
+ * attributes, so `ExtractRecord` resolves to an open record and `records` is a
98
+ * usable `Record<string, unknown> & RecordMetadata` array instead of `never[]`.
99
+ * `build()` returns `never` because there is no definition to build — the
100
+ * string overload never calls it.
101
+ */
102
+ interface UntypedBuilder {
103
+ readonly _types: {
104
+ record: Record<string, unknown>;
105
+ input: Record<string, unknown>;
106
+ update: Record<string, unknown>;
107
+ };
108
+ build(): never;
109
+ }
110
+ /** What `standards.from("object-name")` hands back. */
111
+ type UntypedRecordsQuery = RecordsQuery<UntypedBuilder>;
112
+ /**
113
+ * The four verbs this module needs, typed non-generically: the real `Transport`
114
+ * satisfies it, and so does a hand-rolled stub, which a `Pick` of the generic
115
+ * signatures would reject.
116
+ */
117
+ interface RecordsTransport {
118
+ get(path: string): Promise<unknown>;
119
+ post(path: string, body?: unknown): Promise<unknown>;
120
+ put(path: string, body?: unknown): Promise<unknown>;
121
+ delete(path: string): Promise<unknown>;
122
+ }
123
+ /** A builder gives typed records; a bare object name is the untyped escape hatch. */
124
+ declare function createRecordsQuery<TBuilder extends ObjectBuilderLike>(transport: RecordsTransport, target: TBuilder): RecordsQuery<TBuilder>;
125
+ declare function createRecordsQuery(transport: RecordsTransport, target: string): UntypedRecordsQuery;
126
+
127
+ interface SchemaSourceSummary {
128
+ sourceId: string;
129
+ hash: string;
130
+ appliedAt: string;
131
+ objectNames: string[];
132
+ }
133
+ interface SyncResult {
134
+ applied: boolean;
135
+ hash: string;
136
+ objects?: {
137
+ created: number;
138
+ updated: number;
139
+ deleted: number;
140
+ };
141
+ views?: {
142
+ created: number;
143
+ updated: number;
144
+ adopted: number;
145
+ released: number;
146
+ };
147
+ }
148
+ /**
149
+ * The two verbs a sync needs, typed non-generically: the real `Transport`
150
+ * satisfies it, and so does a hand-rolled stub, which a `Pick` of the generic
151
+ * signatures would reject.
152
+ */
153
+ interface SyncTransport {
154
+ get(path: string): Promise<unknown>;
155
+ put(path: string, body?: unknown): Promise<unknown>;
156
+ }
157
+ /** GET the remote summary; PUT only when the hash differs or the source was never pushed. */
158
+ declare function syncSchemaSource(transport: SyncTransport, source: SchemaSource): Promise<SyncResult>;
159
+
160
+ interface TransportConfig {
161
+ baseUrl: string;
162
+ apiKey: string;
163
+ tenantId?: string;
164
+ timeoutMs?: number;
165
+ }
166
+ declare function createTransport(config: TransportConfig): {
167
+ get<T = unknown>(path: string, params?: Record<string, unknown>): Promise<T>;
168
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
169
+ patch<T = unknown>(path: string, body?: unknown): Promise<T>;
170
+ put<T = unknown>(path: string, body?: unknown): Promise<T>;
171
+ delete<T = unknown>(path: string): Promise<T>;
172
+ getText: (path: string, params?: Record<string, unknown>) => Promise<string>;
173
+ postMultipart: <T>(path: string, form: FormData) => Promise<T>;
174
+ };
175
+ type Transport = ReturnType<typeof createTransport>;
176
+
177
+ interface Standards {
178
+ schema: {
179
+ sync(source: SchemaSource): Promise<SyncResult>;
180
+ };
181
+ from<TBuilder extends ObjectBuilderLike>(builder: TBuilder): RecordsQuery<TBuilder>;
182
+ from(objectName: string): UntypedRecordsQuery;
183
+ }
184
+ declare function createStandards(config: TransportConfig): Standards;
185
+
186
+ /**
187
+ * JSON delivers dates as ISO strings; the client knows the definition, so it
188
+ * revives system timestamps and every `date` attribute so `ExtractRecord` tells
189
+ * the truth on the wire. Nothing else is transformed.
190
+ */
191
+ declare function reviveRecord<T extends Record<string, unknown>>(definition: ObjectDefinition | null, raw: T): T;
192
+
193
+ /** Any non-2xx answer from a Standards API. `code` is the server's `SchemaErrorCode` when it sent one. */
194
+ declare class StandardsRequestError extends Error {
195
+ readonly status: number;
196
+ readonly code?: string;
197
+ constructor(status: number, message: string, code?: string);
198
+ }
199
+ /** 401 or 403: the key is missing, invalid, or lacks the permission the route requires. */
200
+ declare class StandardsAuthError extends StandardsRequestError {
201
+ constructor(status: number, message: string, code?: string);
202
+ }
203
+ /** 409 from `PUT /schema/sources/:id`: a name this source declares is owned by core, a bundle, or another source. */
204
+ declare class SchemaSourceConflictError extends StandardsRequestError {
205
+ /** The object or `object:view` name that clashed. */
206
+ readonly objectName: string;
207
+ /** `core`, `bundle:<id>` or `source:<id>`. */
208
+ readonly owner: string;
209
+ constructor(status: number, message: string, code: string | undefined);
210
+ }
211
+ /** 400 from `PUT /schema/sources/:id`: bad id, oversize payload, hash mismatch, invalid definition, or an unresolved migration. */
212
+ declare class SchemaSourceValidationError extends StandardsRequestError {
213
+ constructor(status: number, message: string, code: string | undefined);
214
+ }
215
+ /** The client was constructed in a way it refuses to run (e.g. an API key in a browser). */
216
+ declare class StandardsConfigurationError extends Error {
217
+ constructor(message: string);
218
+ }
219
+
220
+ export { type FetchResult, type ObjectBuilderLike, type RecordInput, type RecordUpdate, type RecordsQuery, type SchemaSource, SchemaSourceConflictError, type SchemaSourceSummary, SchemaSourceValidationError, type Standards, StandardsAuthError, StandardsConfigurationError, StandardsRequestError, type SyncResult, type Transport, type TransportConfig, type UntypedBuilder, type UntypedRecordsQuery, createRecordsQuery, createStandards, createTransport, defineSchemaSource, hashSchemaSource, reviveRecord, syncSchemaSource };
@@ -0,0 +1,220 @@
1
+ import { ObjectDefinition, ViewDefinition, ExtractRecord, ExtractRecordInputStrict, ExtractRecordUpdateStrict } from '@stndrds/schema';
2
+ export { Attribute, CollectionPage, CollectionResponse, DuplicateError, ExtractRecord, ExtractRecordInput, ExtractRecordInputStrict, ExtractRecordStrict, ExtractRecordUpdate, ExtractRecordUpdateStrict, FilterCombinator, FilterOperator, FilterRule, FilterState, ForbiddenError, ListViewConfig, NotFoundError, ObjectDefinition, RecordMetadata, SCHEMA_SOURCE_ID_PATTERN, SchemaError, SchemaErrorCode, SortDirection, SortRule, ValidationError, ViewConfig, ViewDefinition, assertSchemaSourceId, canonicalStringify, checkbox, currency, date, detailView, document, formula, group, listView, location, multiselect, number, object, phone, relation, richtext, rollup, select, status, text, user } from '@stndrds/schema';
3
+
4
+ /**
5
+ * Anything carrying the fluent builder's phantom types and a `build()` — the
6
+ * `object()` builder. Typed structurally so a consumer never has to import the
7
+ * builder class to satisfy a signature.
8
+ */
9
+ interface ObjectBuilderLike {
10
+ readonly _types: {
11
+ record: unknown;
12
+ input: unknown;
13
+ update: unknown;
14
+ };
15
+ build(): ObjectDefinition;
16
+ }
17
+ type ViewInput = ViewDefinition | {
18
+ build(): ViewDefinition;
19
+ };
20
+ interface SchemaSource<TObjects extends readonly ObjectBuilderLike[] = readonly ObjectBuilderLike[]> {
21
+ readonly id: string;
22
+ /** sha256 hex of `canonicalStringify({ objects, views })` — the same digest the server recomputes. */
23
+ readonly hash: string;
24
+ readonly objects: ObjectDefinition[];
25
+ readonly views: ViewDefinition[];
26
+ /** The builders, kept for type inference in `standards.from(builder)`. */
27
+ readonly builders: TObjects;
28
+ }
29
+ /**
30
+ * Node's `node:crypto` is the only import in this package that is not a Web
31
+ * API; keeping it confined here leaves the rest of the client runtime-agnostic.
32
+ * Node >= 18 is the supported runtime for this slice; edge is a follow-up.
33
+ */
34
+ declare function hashSchemaSource(payload: {
35
+ objects: ObjectDefinition[];
36
+ views: ViewDefinition[];
37
+ }): string;
38
+ /**
39
+ * Declare the schema an external codebase owns on a tenant. Builders are built
40
+ * once here and the hash is computed once; the builders themselves are kept so
41
+ * `from()` can infer record types.
42
+ */
43
+ declare function defineSchemaSource<const TObjects extends readonly ObjectBuilderLike[]>(id: string, config: {
44
+ objects: TObjects;
45
+ views?: ViewInput[];
46
+ }): SchemaSource<TObjects>;
47
+
48
+ type Attr<TBuilder> = keyof ExtractRecord<TBuilder> & string;
49
+ /**
50
+ * The builder's own attributes keep their exact types (a `select` refuses an
51
+ * option it does not declare); runtime-added custom attributes stay writable
52
+ * through the open index. `ExtractRecordInput`'s own index is typed
53
+ * `CustomAttributeValue`, which has no `Date` member and would reject the very
54
+ * date values the REST API accepts — hence the strict variant plus this index.
55
+ */
56
+ type RecordInput<TBuilder> = ExtractRecordInputStrict<TBuilder> & {
57
+ [key: string]: unknown;
58
+ };
59
+ type RecordUpdate<TBuilder> = ExtractRecordUpdateStrict<TBuilder> & {
60
+ [key: string]: unknown;
61
+ };
62
+ /** The scalar shapes the fluent entry points accept; `FilterRule.value` is wider. */
63
+ type RuleValue = string | number | boolean | null;
64
+ interface FetchResult<TBuilder> {
65
+ records: ExtractRecord<TBuilder>[];
66
+ total: number | null;
67
+ }
68
+ interface RecordsQuery<TBuilder> {
69
+ eq(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
70
+ neq(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
71
+ gt(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
72
+ gte(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
73
+ lt(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
74
+ lte(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
75
+ contains(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
76
+ notContains(attribute: Attr<TBuilder>, value: RuleValue): RecordsQuery<TBuilder>;
77
+ startsWith(attribute: Attr<TBuilder>, value: string): RecordsQuery<TBuilder>;
78
+ endsWith(attribute: Attr<TBuilder>, value: string): RecordsQuery<TBuilder>;
79
+ in(attribute: Attr<TBuilder>, values: string[]): RecordsQuery<TBuilder>;
80
+ notIn(attribute: Attr<TBuilder>, values: string[]): RecordsQuery<TBuilder>;
81
+ isEmpty(attribute: Attr<TBuilder>): RecordsQuery<TBuilder>;
82
+ isNotEmpty(attribute: Attr<TBuilder>): RecordsQuery<TBuilder>;
83
+ or(): RecordsQuery<TBuilder>;
84
+ orderBy(attribute: Attr<TBuilder>, direction?: "asc" | "desc"): RecordsQuery<TBuilder>;
85
+ limit(count: number): RecordsQuery<TBuilder>;
86
+ offset(count: number): RecordsQuery<TBuilder>;
87
+ search(query: string): RecordsQuery<TBuilder>;
88
+ fetch(): Promise<FetchResult<TBuilder>>;
89
+ single(): Promise<ExtractRecord<TBuilder> | null>;
90
+ get(id: string): Promise<ExtractRecord<TBuilder>>;
91
+ create(input: RecordInput<TBuilder>): Promise<ExtractRecord<TBuilder>>;
92
+ update(id: string, patch: RecordUpdate<TBuilder>): Promise<ExtractRecord<TBuilder>>;
93
+ delete(id: string): Promise<void>;
94
+ }
95
+ /**
96
+ * The builder shape `from(objectName: string)` stands in for: no declared
97
+ * attributes, so `ExtractRecord` resolves to an open record and `records` is a
98
+ * usable `Record<string, unknown> & RecordMetadata` array instead of `never[]`.
99
+ * `build()` returns `never` because there is no definition to build — the
100
+ * string overload never calls it.
101
+ */
102
+ interface UntypedBuilder {
103
+ readonly _types: {
104
+ record: Record<string, unknown>;
105
+ input: Record<string, unknown>;
106
+ update: Record<string, unknown>;
107
+ };
108
+ build(): never;
109
+ }
110
+ /** What `standards.from("object-name")` hands back. */
111
+ type UntypedRecordsQuery = RecordsQuery<UntypedBuilder>;
112
+ /**
113
+ * The four verbs this module needs, typed non-generically: the real `Transport`
114
+ * satisfies it, and so does a hand-rolled stub, which a `Pick` of the generic
115
+ * signatures would reject.
116
+ */
117
+ interface RecordsTransport {
118
+ get(path: string): Promise<unknown>;
119
+ post(path: string, body?: unknown): Promise<unknown>;
120
+ put(path: string, body?: unknown): Promise<unknown>;
121
+ delete(path: string): Promise<unknown>;
122
+ }
123
+ /** A builder gives typed records; a bare object name is the untyped escape hatch. */
124
+ declare function createRecordsQuery<TBuilder extends ObjectBuilderLike>(transport: RecordsTransport, target: TBuilder): RecordsQuery<TBuilder>;
125
+ declare function createRecordsQuery(transport: RecordsTransport, target: string): UntypedRecordsQuery;
126
+
127
+ interface SchemaSourceSummary {
128
+ sourceId: string;
129
+ hash: string;
130
+ appliedAt: string;
131
+ objectNames: string[];
132
+ }
133
+ interface SyncResult {
134
+ applied: boolean;
135
+ hash: string;
136
+ objects?: {
137
+ created: number;
138
+ updated: number;
139
+ deleted: number;
140
+ };
141
+ views?: {
142
+ created: number;
143
+ updated: number;
144
+ adopted: number;
145
+ released: number;
146
+ };
147
+ }
148
+ /**
149
+ * The two verbs a sync needs, typed non-generically: the real `Transport`
150
+ * satisfies it, and so does a hand-rolled stub, which a `Pick` of the generic
151
+ * signatures would reject.
152
+ */
153
+ interface SyncTransport {
154
+ get(path: string): Promise<unknown>;
155
+ put(path: string, body?: unknown): Promise<unknown>;
156
+ }
157
+ /** GET the remote summary; PUT only when the hash differs or the source was never pushed. */
158
+ declare function syncSchemaSource(transport: SyncTransport, source: SchemaSource): Promise<SyncResult>;
159
+
160
+ interface TransportConfig {
161
+ baseUrl: string;
162
+ apiKey: string;
163
+ tenantId?: string;
164
+ timeoutMs?: number;
165
+ }
166
+ declare function createTransport(config: TransportConfig): {
167
+ get<T = unknown>(path: string, params?: Record<string, unknown>): Promise<T>;
168
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
169
+ patch<T = unknown>(path: string, body?: unknown): Promise<T>;
170
+ put<T = unknown>(path: string, body?: unknown): Promise<T>;
171
+ delete<T = unknown>(path: string): Promise<T>;
172
+ getText: (path: string, params?: Record<string, unknown>) => Promise<string>;
173
+ postMultipart: <T>(path: string, form: FormData) => Promise<T>;
174
+ };
175
+ type Transport = ReturnType<typeof createTransport>;
176
+
177
+ interface Standards {
178
+ schema: {
179
+ sync(source: SchemaSource): Promise<SyncResult>;
180
+ };
181
+ from<TBuilder extends ObjectBuilderLike>(builder: TBuilder): RecordsQuery<TBuilder>;
182
+ from(objectName: string): UntypedRecordsQuery;
183
+ }
184
+ declare function createStandards(config: TransportConfig): Standards;
185
+
186
+ /**
187
+ * JSON delivers dates as ISO strings; the client knows the definition, so it
188
+ * revives system timestamps and every `date` attribute so `ExtractRecord` tells
189
+ * the truth on the wire. Nothing else is transformed.
190
+ */
191
+ declare function reviveRecord<T extends Record<string, unknown>>(definition: ObjectDefinition | null, raw: T): T;
192
+
193
+ /** Any non-2xx answer from a Standards API. `code` is the server's `SchemaErrorCode` when it sent one. */
194
+ declare class StandardsRequestError extends Error {
195
+ readonly status: number;
196
+ readonly code?: string;
197
+ constructor(status: number, message: string, code?: string);
198
+ }
199
+ /** 401 or 403: the key is missing, invalid, or lacks the permission the route requires. */
200
+ declare class StandardsAuthError extends StandardsRequestError {
201
+ constructor(status: number, message: string, code?: string);
202
+ }
203
+ /** 409 from `PUT /schema/sources/:id`: a name this source declares is owned by core, a bundle, or another source. */
204
+ declare class SchemaSourceConflictError extends StandardsRequestError {
205
+ /** The object or `object:view` name that clashed. */
206
+ readonly objectName: string;
207
+ /** `core`, `bundle:<id>` or `source:<id>`. */
208
+ readonly owner: string;
209
+ constructor(status: number, message: string, code: string | undefined);
210
+ }
211
+ /** 400 from `PUT /schema/sources/:id`: bad id, oversize payload, hash mismatch, invalid definition, or an unresolved migration. */
212
+ declare class SchemaSourceValidationError extends StandardsRequestError {
213
+ constructor(status: number, message: string, code: string | undefined);
214
+ }
215
+ /** The client was constructed in a way it refuses to run (e.g. an API key in a browser). */
216
+ declare class StandardsConfigurationError extends Error {
217
+ constructor(message: string);
218
+ }
219
+
220
+ export { type FetchResult, type ObjectBuilderLike, type RecordInput, type RecordUpdate, type RecordsQuery, type SchemaSource, SchemaSourceConflictError, type SchemaSourceSummary, SchemaSourceValidationError, type Standards, StandardsAuthError, StandardsConfigurationError, StandardsRequestError, type SyncResult, type Transport, type TransportConfig, type UntypedBuilder, type UntypedRecordsQuery, createRecordsQuery, createStandards, createTransport, defineSchemaSource, hashSchemaSource, reviveRecord, syncSchemaSource };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var schema=require('@stndrds/schema'),crypto=require('crypto');/*! © 2025-2026 AND YOU CREATE SAS (RCS Paris 940 190 614). Proprietary — see LICENSE (Standards SDK License 1.0). */
2
+ var V=Object.defineProperty;var n=(r,e)=>V(r,"name",{value:e,configurable:true});var c=class extends Error{static{n(this,"StandardsRequestError");}constructor(e,t,i){super(t),this.name="StandardsRequestError",this.status=e,this.code=i;}},f=class extends c{static{n(this,"StandardsAuthError");}constructor(e,t,i){super(e,t,i),this.name="StandardsAuthError";}},T=class extends c{static{n(this,"SchemaSourceConflictError");}constructor(e,t,i){super(e,t,i),this.name="SchemaSourceConflictError";let o=/"([^"]+)" is owned by (\S+)/.exec(t);this.objectName=o?.[1]??"",this.owner=o?.[2]??"";}},R=class extends c{static{n(this,"SchemaSourceValidationError");}constructor(e,t,i){super(e,t,i),this.name="SchemaSourceValidationError";}},g=class extends Error{static{n(this,"StandardsConfigurationError");}constructor(e){super(e),this.name="StandardsConfigurationError";}};var C=["createdAt","updatedAt","deletedAt"];function D(r){let e=new Set;for(let t of r?.attributes??[])t.type==="date"&&e.add(t.name);return e}n(D,"dateAttributeNames");function I(r){return typeof r=="object"&&r!==null&&"start"in r&&"end"in r}n(I,"isRange");function U(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}n(U,"isRecord");function _(r){let{values:e,...t}=r;return {...t,...U(e)?e:{}}}n(_,"flattenValues");function S(r){return typeof r=="string"?new Date(r):I(r)?{start:S(r.start),end:S(r.end)}:r}n(S,"toDate");function k(r,e){let t=D(r),i=_(e);for(let o of C)o in i&&(i[o]=S(i[o]));for(let o of t)o in i&&(i[o]=S(i[o]));return i}n(k,"reviveRecord");function E(r){return Object.fromEntries(Object.entries(r).map(([e,t])=>[e,t instanceof Date?t.toISOString():t]))}n(E,"serializeInput");function b(r,...e){return ["/records",...[r,...e].map(F)].join("/")}n(b,"recordsPath");function F(r){if(r===""||r==="."||r==="..")throw new schema.ValidationError(`Invalid path segment "${r}" in a records request`,[]);return encodeURIComponent(r)}n(F,"encodePathSegment");var B=class r{constructor(e,t,i,o={combinator:"and",rules:[],sorts:[]}){this.transport=e;this.objectName=t;this.definition=i;this.state=o;}static{n(this,"RecordsQueryImpl");}with(e){return new r(this.transport,this.objectName,this.definition,{...this.state,...e})}rule(e,t,i){return this.with({rules:[...this.state.rules,{attribute:e,operator:t,value:i}]})}eq(e,t){return this.rule(e,"is",t)}neq(e,t){return this.rule(e,"is_not",t)}gt(e,t){return this.rule(e,"greater_than",t)}gte(e,t){return this.rule(e,"greater_or_equal",t)}lt(e,t){return this.rule(e,"less_than",t)}lte(e,t){return this.rule(e,"less_or_equal",t)}contains(e,t){return this.rule(e,"contains",t)}notContains(e,t){return this.rule(e,"not_contains",t)}startsWith(e,t){return this.rule(e,"starts_with",t)}endsWith(e,t){return this.rule(e,"ends_with",t)}in(e,t){return this.rule(e,"any_of",t)}notIn(e,t){return this.rule(e,"none_of",t)}isEmpty(e){return this.rule(e,"is_empty",null)}isNotEmpty(e){return this.rule(e,"is_not_empty",null)}or(){return this.with({combinator:"or"})}orderBy(e,t="asc"){return this.with({sorts:[...this.state.sorts,{attribute:e,direction:t}]})}limit(e){return this.with({limit:e})}offset(e){return this.with({offset:e})}search(e){return this.with({search:e})}body(){let e={};return this.state.search!==void 0&&(e.q=this.state.search),this.state.rules.length>0&&(e.filters={combinator:this.state.combinator,rules:this.state.rules}),this.state.sorts.length>0&&(e.sorts=this.state.sorts),this.state.limit!==void 0&&(e.limit=this.state.limit),this.state.offset!==void 0&&(e.offset=this.state.offset),e}revive(e){return k(this.definition,e)}async fetch(){let e=this.state.search!==void 0?"search":"list",t=await this.transport.post(b(this.objectName,e),this.body());return {records:t.data.map(i=>this.revive(i)),total:t.page.total}}async single(){let{records:e}=await this.limit(1).fetch();return e[0]??null}async get(e){let t=await this.transport.get(b(this.objectName,e));return this.revive(t)}async create(e){let t=await this.transport.post(b(this.objectName),{data:E(e)});return this.revive(t)}async update(e,t){let i=await this.transport.put(b(this.objectName,e),E(t));return this.revive(i)}async delete(e){await this.transport.delete(b(this.objectName,e));}};function x(r,e){if(typeof e=="string")return new B(r,e,null);let t=e.build();return new B(r,t.name,t)}n(x,"createRecordsQuery");async function j(r,e){let t=`/schema/sources/${encodeURIComponent(e.id)}`,i;try{i=await r.get(t);}catch(o){if(o instanceof c&&o.status===404)i=null;else throw o}if(i?.hash===e.hash)return {applied:false,hash:e.hash};try{return await r.put(t,{hash:e.hash,objects:e.objects,views:e.views})}catch(o){throw o instanceof c&&o.status===409?new T(o.status,o.message,o.code):o instanceof c&&o.status===400?new R(o.status,o.message,o.code):o}}n(j,"syncSchemaSource");function A(r,e,t){let i=r.endsWith("/")?r:`${r}/`,o=e.replace(/^\/+/,""),l=new URL(o,i);if(t)for(let[w,h]of Object.entries(t))h!=null&&l.searchParams.set(w,String(h));return l.toString()}n(A,"buildUrl");function O(r){let{baseUrl:e,apiKey:t,tenantId:i,timeoutMs:o=3e4}=r,l={Authorization:`Bearer ${t}`,"Content-Type":"application/json",...i?{"X-Tenant-ID":i}:{}};async function w(u,a){try{return await fetch(u,{...a,signal:AbortSignal.timeout(o)})}catch(s){throw s instanceof TypeError||s instanceof DOMException&&s.name==="TimeoutError"?new c(0,`Could not connect to ${e}`):s}}n(w,"fetchOrThrow");async function h(u,a){let s=await w(u,a);if(s.status===204)return null;let d=await s.json();if(!s.ok){let p=d,m=p.message??`Request failed with status ${s.status}`;throw s.status===401||s.status===403?new f(s.status,m,p.code):new c(s.status,m,p.code)}return d}n(h,"sendRequest");function y(u,a,s){let d=A(e,a,s?.params);return h(d,{method:u,headers:l,body:s?.body?JSON.stringify(s.body):void 0})}n(y,"request");async function v(u,a){let s=A(e,u,a),d=await w(s,{method:"GET",headers:l}),p=await d.text();if(!d.ok){let m=`Request failed with status ${d.status}`;try{m=JSON.parse(p).message??m;}catch{p&&(m=p);}throw d.status===401||d.status===403?new f(d.status,m):new c(d.status,m)}return p}n(v,"getText");function Q(u,a){let s=A(e,u),{"Content-Type":d,...p}=l;return h(s,{method:"POST",headers:p,body:a})}return n(Q,"postMultipart"),{get(u,a){return y("GET",u,{params:a})},post(u,a){return y("POST",u,{body:a})},patch(u,a){return y("PATCH",u,{body:a})},put(u,a){return y("PUT",u,{body:a})},delete(u){return y("DELETE",u)},getText:v,postMultipart:Q}}n(O,"createTransport");function N(r){if(typeof globalThis.window<"u"&&r.apiKey)throw new g("createStandards received an apiKey in a browser context. API keys belong to server code only (Route Handlers, Server Components, scripts).");let e=O(r);return {schema:{sync:n(t=>j(e,t),"sync")},from:n((t=>typeof t=="string"?x(e,t):x(e,t)),"from")}}n(N,"createStandards");function H(r){return "build"in r&&typeof r.build=="function"?r.build():r}n(H,"buildView");function P(r){return crypto.createHash("sha256").update(schema.canonicalStringify(r)).digest("hex")}n(P,"hashSchemaSource");function K(r,e){schema.assertSchemaSourceId(r);let t=e.objects.map(l=>l.build()),i=new Set;for(let l of t){if(i.has(l.name))throw new schema.DuplicateError("object",l.name);i.add(l.name);}let o=(e.views??[]).map(H);return {id:r,hash:P({objects:t,views:o}),objects:t,views:o,builders:e.objects}}n(K,"defineSchemaSource");Object.defineProperty(exports,"DuplicateError",{enumerable:true,get:function(){return schema.DuplicateError}});Object.defineProperty(exports,"ForbiddenError",{enumerable:true,get:function(){return schema.ForbiddenError}});Object.defineProperty(exports,"NotFoundError",{enumerable:true,get:function(){return schema.NotFoundError}});Object.defineProperty(exports,"SCHEMA_SOURCE_ID_PATTERN",{enumerable:true,get:function(){return schema.SCHEMA_SOURCE_ID_PATTERN}});Object.defineProperty(exports,"SchemaError",{enumerable:true,get:function(){return schema.SchemaError}});Object.defineProperty(exports,"SchemaErrorCode",{enumerable:true,get:function(){return schema.SchemaErrorCode}});Object.defineProperty(exports,"ValidationError",{enumerable:true,get:function(){return schema.ValidationError}});Object.defineProperty(exports,"assertSchemaSourceId",{enumerable:true,get:function(){return schema.assertSchemaSourceId}});Object.defineProperty(exports,"canonicalStringify",{enumerable:true,get:function(){return schema.canonicalStringify}});Object.defineProperty(exports,"checkbox",{enumerable:true,get:function(){return schema.checkbox}});Object.defineProperty(exports,"currency",{enumerable:true,get:function(){return schema.currency}});Object.defineProperty(exports,"date",{enumerable:true,get:function(){return schema.date}});Object.defineProperty(exports,"detailView",{enumerable:true,get:function(){return schema.detailView}});Object.defineProperty(exports,"document",{enumerable:true,get:function(){return schema.document}});Object.defineProperty(exports,"formula",{enumerable:true,get:function(){return schema.formula}});Object.defineProperty(exports,"group",{enumerable:true,get:function(){return schema.group}});Object.defineProperty(exports,"listView",{enumerable:true,get:function(){return schema.listView}});Object.defineProperty(exports,"location",{enumerable:true,get:function(){return schema.location}});Object.defineProperty(exports,"multiselect",{enumerable:true,get:function(){return schema.multiselect}});Object.defineProperty(exports,"number",{enumerable:true,get:function(){return schema.number}});Object.defineProperty(exports,"object",{enumerable:true,get:function(){return schema.object}});Object.defineProperty(exports,"phone",{enumerable:true,get:function(){return schema.phone}});Object.defineProperty(exports,"relation",{enumerable:true,get:function(){return schema.relation}});Object.defineProperty(exports,"richtext",{enumerable:true,get:function(){return schema.richtext}});Object.defineProperty(exports,"rollup",{enumerable:true,get:function(){return schema.rollup}});Object.defineProperty(exports,"select",{enumerable:true,get:function(){return schema.select}});Object.defineProperty(exports,"status",{enumerable:true,get:function(){return schema.status}});Object.defineProperty(exports,"text",{enumerable:true,get:function(){return schema.text}});Object.defineProperty(exports,"user",{enumerable:true,get:function(){return schema.user}});exports.SchemaSourceConflictError=T;exports.SchemaSourceValidationError=R;exports.StandardsAuthError=f;exports.StandardsConfigurationError=g;exports.StandardsRequestError=c;exports.createRecordsQuery=x;exports.createStandards=N;exports.createTransport=O;exports.defineSchemaSource=K;exports.hashSchemaSource=P;exports.reviveRecord=k;exports.syncSchemaSource=j;
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import {ValidationError,canonicalStringify,assertSchemaSourceId,DuplicateError}from'@stndrds/schema';export{DuplicateError,ForbiddenError,NotFoundError,SCHEMA_SOURCE_ID_PATTERN,SchemaError,SchemaErrorCode,ValidationError,assertSchemaSourceId,canonicalStringify,checkbox,currency,date,detailView,document,formula,group,listView,location,multiselect,number,object,phone,relation,richtext,rollup,select,status,text,user}from'@stndrds/schema';import {createHash}from'crypto';/*! © 2025-2026 AND YOU CREATE SAS (RCS Paris 940 190 614). Proprietary — see LICENSE (Standards SDK License 1.0). */
2
+ var V=Object.defineProperty;var n=(r,e)=>V(r,"name",{value:e,configurable:true});var c=class extends Error{static{n(this,"StandardsRequestError");}constructor(e,t,i){super(t),this.name="StandardsRequestError",this.status=e,this.code=i;}},f=class extends c{static{n(this,"StandardsAuthError");}constructor(e,t,i){super(e,t,i),this.name="StandardsAuthError";}},T=class extends c{static{n(this,"SchemaSourceConflictError");}constructor(e,t,i){super(e,t,i),this.name="SchemaSourceConflictError";let o=/"([^"]+)" is owned by (\S+)/.exec(t);this.objectName=o?.[1]??"",this.owner=o?.[2]??"";}},R=class extends c{static{n(this,"SchemaSourceValidationError");}constructor(e,t,i){super(e,t,i),this.name="SchemaSourceValidationError";}},g=class extends Error{static{n(this,"StandardsConfigurationError");}constructor(e){super(e),this.name="StandardsConfigurationError";}};var C=["createdAt","updatedAt","deletedAt"];function D(r){let e=new Set;for(let t of r?.attributes??[])t.type==="date"&&e.add(t.name);return e}n(D,"dateAttributeNames");function I(r){return typeof r=="object"&&r!==null&&"start"in r&&"end"in r}n(I,"isRange");function U(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}n(U,"isRecord");function _(r){let{values:e,...t}=r;return {...t,...U(e)?e:{}}}n(_,"flattenValues");function S(r){return typeof r=="string"?new Date(r):I(r)?{start:S(r.start),end:S(r.end)}:r}n(S,"toDate");function k(r,e){let t=D(r),i=_(e);for(let o of C)o in i&&(i[o]=S(i[o]));for(let o of t)o in i&&(i[o]=S(i[o]));return i}n(k,"reviveRecord");function E(r){return Object.fromEntries(Object.entries(r).map(([e,t])=>[e,t instanceof Date?t.toISOString():t]))}n(E,"serializeInput");function b(r,...e){return ["/records",...[r,...e].map(F)].join("/")}n(b,"recordsPath");function F(r){if(r===""||r==="."||r==="..")throw new ValidationError(`Invalid path segment "${r}" in a records request`,[]);return encodeURIComponent(r)}n(F,"encodePathSegment");var B=class r{constructor(e,t,i,o={combinator:"and",rules:[],sorts:[]}){this.transport=e;this.objectName=t;this.definition=i;this.state=o;}static{n(this,"RecordsQueryImpl");}with(e){return new r(this.transport,this.objectName,this.definition,{...this.state,...e})}rule(e,t,i){return this.with({rules:[...this.state.rules,{attribute:e,operator:t,value:i}]})}eq(e,t){return this.rule(e,"is",t)}neq(e,t){return this.rule(e,"is_not",t)}gt(e,t){return this.rule(e,"greater_than",t)}gte(e,t){return this.rule(e,"greater_or_equal",t)}lt(e,t){return this.rule(e,"less_than",t)}lte(e,t){return this.rule(e,"less_or_equal",t)}contains(e,t){return this.rule(e,"contains",t)}notContains(e,t){return this.rule(e,"not_contains",t)}startsWith(e,t){return this.rule(e,"starts_with",t)}endsWith(e,t){return this.rule(e,"ends_with",t)}in(e,t){return this.rule(e,"any_of",t)}notIn(e,t){return this.rule(e,"none_of",t)}isEmpty(e){return this.rule(e,"is_empty",null)}isNotEmpty(e){return this.rule(e,"is_not_empty",null)}or(){return this.with({combinator:"or"})}orderBy(e,t="asc"){return this.with({sorts:[...this.state.sorts,{attribute:e,direction:t}]})}limit(e){return this.with({limit:e})}offset(e){return this.with({offset:e})}search(e){return this.with({search:e})}body(){let e={};return this.state.search!==void 0&&(e.q=this.state.search),this.state.rules.length>0&&(e.filters={combinator:this.state.combinator,rules:this.state.rules}),this.state.sorts.length>0&&(e.sorts=this.state.sorts),this.state.limit!==void 0&&(e.limit=this.state.limit),this.state.offset!==void 0&&(e.offset=this.state.offset),e}revive(e){return k(this.definition,e)}async fetch(){let e=this.state.search!==void 0?"search":"list",t=await this.transport.post(b(this.objectName,e),this.body());return {records:t.data.map(i=>this.revive(i)),total:t.page.total}}async single(){let{records:e}=await this.limit(1).fetch();return e[0]??null}async get(e){let t=await this.transport.get(b(this.objectName,e));return this.revive(t)}async create(e){let t=await this.transport.post(b(this.objectName),{data:E(e)});return this.revive(t)}async update(e,t){let i=await this.transport.put(b(this.objectName,e),E(t));return this.revive(i)}async delete(e){await this.transport.delete(b(this.objectName,e));}};function x(r,e){if(typeof e=="string")return new B(r,e,null);let t=e.build();return new B(r,t.name,t)}n(x,"createRecordsQuery");async function j(r,e){let t=`/schema/sources/${encodeURIComponent(e.id)}`,i;try{i=await r.get(t);}catch(o){if(o instanceof c&&o.status===404)i=null;else throw o}if(i?.hash===e.hash)return {applied:false,hash:e.hash};try{return await r.put(t,{hash:e.hash,objects:e.objects,views:e.views})}catch(o){throw o instanceof c&&o.status===409?new T(o.status,o.message,o.code):o instanceof c&&o.status===400?new R(o.status,o.message,o.code):o}}n(j,"syncSchemaSource");function A(r,e,t){let i=r.endsWith("/")?r:`${r}/`,o=e.replace(/^\/+/,""),l=new URL(o,i);if(t)for(let[w,h]of Object.entries(t))h!=null&&l.searchParams.set(w,String(h));return l.toString()}n(A,"buildUrl");function O(r){let{baseUrl:e,apiKey:t,tenantId:i,timeoutMs:o=3e4}=r,l={Authorization:`Bearer ${t}`,"Content-Type":"application/json",...i?{"X-Tenant-ID":i}:{}};async function w(u,a){try{return await fetch(u,{...a,signal:AbortSignal.timeout(o)})}catch(s){throw s instanceof TypeError||s instanceof DOMException&&s.name==="TimeoutError"?new c(0,`Could not connect to ${e}`):s}}n(w,"fetchOrThrow");async function h(u,a){let s=await w(u,a);if(s.status===204)return null;let d=await s.json();if(!s.ok){let p=d,m=p.message??`Request failed with status ${s.status}`;throw s.status===401||s.status===403?new f(s.status,m,p.code):new c(s.status,m,p.code)}return d}n(h,"sendRequest");function y(u,a,s){let d=A(e,a,s?.params);return h(d,{method:u,headers:l,body:s?.body?JSON.stringify(s.body):void 0})}n(y,"request");async function v(u,a){let s=A(e,u,a),d=await w(s,{method:"GET",headers:l}),p=await d.text();if(!d.ok){let m=`Request failed with status ${d.status}`;try{m=JSON.parse(p).message??m;}catch{p&&(m=p);}throw d.status===401||d.status===403?new f(d.status,m):new c(d.status,m)}return p}n(v,"getText");function Q(u,a){let s=A(e,u),{"Content-Type":d,...p}=l;return h(s,{method:"POST",headers:p,body:a})}return n(Q,"postMultipart"),{get(u,a){return y("GET",u,{params:a})},post(u,a){return y("POST",u,{body:a})},patch(u,a){return y("PATCH",u,{body:a})},put(u,a){return y("PUT",u,{body:a})},delete(u){return y("DELETE",u)},getText:v,postMultipart:Q}}n(O,"createTransport");function N(r){if(typeof globalThis.window<"u"&&r.apiKey)throw new g("createStandards received an apiKey in a browser context. API keys belong to server code only (Route Handlers, Server Components, scripts).");let e=O(r);return {schema:{sync:n(t=>j(e,t),"sync")},from:n((t=>typeof t=="string"?x(e,t):x(e,t)),"from")}}n(N,"createStandards");function H(r){return "build"in r&&typeof r.build=="function"?r.build():r}n(H,"buildView");function P(r){return createHash("sha256").update(canonicalStringify(r)).digest("hex")}n(P,"hashSchemaSource");function K(r,e){assertSchemaSourceId(r);let t=e.objects.map(l=>l.build()),i=new Set;for(let l of t){if(i.has(l.name))throw new DuplicateError("object",l.name);i.add(l.name);}let o=(e.views??[]).map(H);return {id:r,hash:P({objects:t,views:o}),objects:t,views:o,builders:e.objects}}n(K,"defineSchemaSource");export{T as SchemaSourceConflictError,R as SchemaSourceValidationError,f as StandardsAuthError,g as StandardsConfigurationError,c as StandardsRequestError,x as createRecordsQuery,N as createStandards,O as createTransport,K as defineSchemaSource,P as hashSchemaSource,k as reviveRecord,j as syncSchemaSource};
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@stndrds/client",
3
+ "version": "1.0.0-alpha.295",
4
+ "description": "Public TypeScript client for Standards: declare schema sources with the fluent builders, sync them to a hosted tenant, and operate typed records over the REST API.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "sideEffects": false,
16
+ "files": [
17
+ "dist",
18
+ "LICENSE",
19
+ "README.md"
20
+ ],
21
+ "dependencies": {
22
+ "@stndrds/schema": "1.0.0-alpha.295"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^25.6.0",
26
+ "tsup": "^8.3.5",
27
+ "typescript": "^5.6.3",
28
+ "vitest": "^4.1.11"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "registry": "https://registry.npmjs.org/"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/andyoucreate/standards.git",
37
+ "directory": "packages/client"
38
+ },
39
+ "license": "SEE LICENSE IN LICENSE",
40
+ "author": "AndYouCreate",
41
+ "homepage": "https://docs.standards.new",
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "dev": "tsup --watch",
45
+ "lint": "biome check .",
46
+ "test": "vitest run",
47
+ "typecheck": "tsc --noEmit",
48
+ "typecheck:tests": "tsc --noEmit -p tsconfig.test.json",
49
+ "clean": "rm -rf dist"
50
+ }
51
+ }