@drhomet/mithaq 0.0.0-stage → 0.2.0

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.
Files changed (152) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSES/openstatus-time-picker.txt +21 -0
  3. package/LICENSES/payload.txt +22 -0
  4. package/LICENSES/ponytail.txt +21 -0
  5. package/README.md +160 -2
  6. package/dist/asas/actor.d.ts +96 -0
  7. package/dist/asas/actor.js +114 -0
  8. package/dist/asas/build.d.ts +239 -0
  9. package/dist/asas/build.js +834 -0
  10. package/dist/asas/cli/mithaq.d.ts +2 -0
  11. package/dist/asas/cli/mithaq.js +177 -0
  12. package/dist/asas/cli/upgrade.d.ts +33 -0
  13. package/dist/asas/cli/upgrade.js +256 -0
  14. package/dist/asas/http/fetch.d.ts +41 -0
  15. package/dist/asas/http/fetch.js +178 -0
  16. package/dist/asas/http/orpc.d.ts +45 -0
  17. package/dist/asas/http/orpc.js +153 -0
  18. package/dist/asas/runner/bulk.d.ts +20 -0
  19. package/dist/asas/runner/bulk.js +196 -0
  20. package/dist/asas/runner/errors.d.ts +63 -0
  21. package/dist/asas/runner/errors.js +75 -0
  22. package/dist/asas/runner/read.d.ts +72 -0
  23. package/dist/asas/runner/read.js +601 -0
  24. package/dist/asas/runner/runtime.d.ts +21 -0
  25. package/dist/asas/runner/runtime.js +8 -0
  26. package/dist/asas/runner/steps.d.ts +418 -0
  27. package/dist/asas/runner/steps.js +277 -0
  28. package/dist/asas/runner/store.d.ts +4 -0
  29. package/dist/asas/runner/store.js +21 -0
  30. package/dist/asas/runner/tenant.d.ts +54 -0
  31. package/dist/asas/runner/tenant.js +275 -0
  32. package/dist/asas/runner/write.d.ts +606 -0
  33. package/dist/asas/runner/write.js +1019 -0
  34. package/dist/auth/better-auth/access.d.ts +29 -0
  35. package/dist/auth/better-auth/access.js +103 -0
  36. package/dist/auth/better-auth/generate.d.ts +31 -0
  37. package/dist/auth/better-auth/generate.js +328 -0
  38. package/dist/auth/better-auth/index.d.ts +94 -0
  39. package/dist/auth/better-auth/index.js +81 -0
  40. package/dist/auth/better-auth/tenant.d.ts +26 -0
  41. package/dist/auth/better-auth/tenant.js +34 -0
  42. package/dist/auth/local/index.d.ts +285 -0
  43. package/dist/auth/local/index.js +738 -0
  44. package/dist/base/decimal.d.ts +9 -0
  45. package/dist/base/decimal.js +16 -0
  46. package/dist/base/fields.d.ts +124 -0
  47. package/dist/base/fields.js +52 -0
  48. package/dist/base/index.d.ts +47 -0
  49. package/dist/base/index.js +5 -0
  50. package/dist/base/json-schema.d.ts +7 -0
  51. package/dist/base/json-schema.js +35 -0
  52. package/dist/base/kinds.d.ts +75 -0
  53. package/dist/base/kinds.js +123 -0
  54. package/dist/base/rules.d.ts +73 -0
  55. package/dist/base/rules.js +81 -0
  56. package/dist/drizzle/index.d.ts +308 -0
  57. package/dist/drizzle/index.js +951 -0
  58. package/dist/drizzle/kinds.d.ts +28 -0
  59. package/dist/drizzle/kinds.js +43 -0
  60. package/dist/drizzle/migrate.d.ts +159 -0
  61. package/dist/drizzle/migrate.js +1328 -0
  62. package/dist/drizzle/proxy/index.d.ts +1 -0
  63. package/dist/drizzle/proxy/index.js +1 -0
  64. package/dist/drizzle/proxy/node-postgres.d.ts +1 -0
  65. package/dist/drizzle/proxy/node-postgres.js +1 -0
  66. package/dist/drizzle/proxy/pg-core.d.ts +1 -0
  67. package/dist/drizzle/proxy/pg-core.js +1 -0
  68. package/dist/drizzle/rules.d.ts +336 -0
  69. package/dist/drizzle/rules.js +44 -0
  70. package/dist/index.d.ts +35 -0
  71. package/dist/index.js +26 -0
  72. package/dist/mithaq/define.d.ts +335 -0
  73. package/dist/mithaq/define.js +57 -0
  74. package/dist/mithaq/generate-messages.d.ts +38 -0
  75. package/dist/mithaq/generate-messages.js +235 -0
  76. package/dist/mithaq/language.d.ts +374 -0
  77. package/dist/mithaq/language.js +76 -0
  78. package/dist/mithaq/lock.d.ts +71 -0
  79. package/dist/mithaq/lock.js +329 -0
  80. package/dist/mithaq/message-file.d.ts +13 -0
  81. package/dist/mithaq/message-file.js +29 -0
  82. package/dist/mithaq/messages.d.ts +55 -0
  83. package/dist/mithaq/messages.js +111 -0
  84. package/dist/mithaq/refusal.d.ts +6 -0
  85. package/dist/mithaq/refusal.js +6 -0
  86. package/dist/mithaq/resolve.d.ts +10 -0
  87. package/dist/mithaq/resolve.js +1014 -0
  88. package/dist/mithaq/rows.d.ts +87 -0
  89. package/dist/mithaq/rows.js +174 -0
  90. package/dist/mithaq/rukn.d.ts +72 -0
  91. package/dist/mithaq/rukn.js +15 -0
  92. package/dist/mithaq/standard-schema.d.ts +38 -0
  93. package/dist/mithaq/standard-schema.js +2 -0
  94. package/dist/mithaq/types.d.ts +386 -0
  95. package/dist/mithaq/types.js +15 -0
  96. package/dist/mithaq/user.d.ts +19 -0
  97. package/dist/mithaq/user.js +16 -0
  98. package/dist/mithaq/where.d.ts +32 -0
  99. package/dist/mithaq/where.js +87 -0
  100. package/dist/rbac/index.d.ts +105 -0
  101. package/dist/rbac/index.js +182 -0
  102. package/dist/rbac/steps.d.ts +22 -0
  103. package/dist/rbac/steps.js +161 -0
  104. package/dist/rich-text/index.d.ts +28 -0
  105. package/dist/rich-text/index.js +59 -0
  106. package/dist/rich-text/rich-text.d.ts +106 -0
  107. package/dist/rich-text/rich-text.js +249 -0
  108. package/dist/rich-text/value.d.ts +55 -0
  109. package/dist/rich-text/value.js +126 -0
  110. package/dist/screen/client.d.ts +156 -0
  111. package/dist/screen/client.js +71 -0
  112. package/dist/screen/generate.d.ts +14 -0
  113. package/dist/screen/generate.js +268 -0
  114. package/dist/screen/index.d.ts +496 -0
  115. package/dist/screen/index.js +663 -0
  116. package/dist/screen/react.d.ts +97 -0
  117. package/dist/screen/react.js +58 -0
  118. package/dist/screen/studio.d.ts +24 -0
  119. package/dist/screen/studio.js +68 -0
  120. package/dist/service/index.d.ts +63 -0
  121. package/dist/service/index.js +15 -0
  122. package/dist/storage/allowed-type.d.ts +5 -0
  123. package/dist/storage/allowed-type.js +7 -0
  124. package/dist/storage/index.d.ts +42 -0
  125. package/dist/storage/index.js +43 -0
  126. package/dist/storage/storage.d.ts +386 -0
  127. package/dist/storage/storage.js +412 -0
  128. package/dist/targets.d.ts +14 -0
  129. package/dist/targets.js +16 -0
  130. package/dist/versions/index.d.ts +37 -0
  131. package/dist/versions/index.js +8 -0
  132. package/dist/versions/versions.d.ts +104 -0
  133. package/dist/versions/versions.js +160 -0
  134. package/dist/vite.d.ts +7 -0
  135. package/dist/vite.js +81 -0
  136. package/dist/zod/index.d.ts +193 -0
  137. package/dist/zod/index.js +122 -0
  138. package/dist/zod/kinds.d.ts +36 -0
  139. package/dist/zod/kinds.js +58 -0
  140. package/dist/zod/rules.d.ts +117 -0
  141. package/dist/zod/rules.js +67 -0
  142. package/docs/packages/mithaq/README.md +962 -0
  143. package/docs/packages/mithaq/src/asas/cli/README.md +252 -0
  144. package/docs/packages/mithaq/src/asas/runner/README.md +472 -0
  145. package/docs/packages/mithaq/src/drizzle/README.md +39 -0
  146. package/docs/packages/mithaq-admin/README.md +312 -0
  147. package/docs/templates/asas/README.md +1056 -0
  148. package/package.json +92 -4
  149. package/skills/add-field/SKILL.md +27 -0
  150. package/skills/add-kayan/SKILL.md +26 -0
  151. package/skills/eject-admin-piece/SKILL.md +28 -0
  152. package/skills/upgrade-mithaq/SKILL.md +35 -0
@@ -0,0 +1,87 @@
1
+ import { type Ref } from './user.ts';
2
+ import { type Operator, type Scalar } from './where.ts';
3
+ import type { Access, FieldAccess } from './types.ts';
4
+ export type RowOperation = 'create' | 'delete' | 'read' | 'update';
5
+ /** What a row compares a field with: a literal, or a reference to the actor's value at one key, resolved before the store sees it. */
6
+ export type RowFieldWhere = Partial<Record<Operator, Scalar | Scalar[] | boolean | Ref>>;
7
+ /** A `Where` whose paths are the kayan's field names plus one relationship hop. */
8
+ export type RowWhere<Paths extends string = string> = {
9
+ and: readonly RowWhere<Paths>[];
10
+ } | {
11
+ or: readonly RowWhere<Paths>[];
12
+ } | {
13
+ [Path in Paths]?: RowFieldWhere;
14
+ };
15
+ type NonEmpty<Answer> = Answer extends boolean | readonly unknown[] ? Answer : keyof Answer extends never ? never : Answer;
16
+ /** A row table checked before it compiles: the role keys, and no empty `Where`. `'*'` is the row every assigned actor gets beside its own. */
17
+ export type CheckedTable<Table, Role extends string = string> = {
18
+ [Key in keyof Table]: Key extends '*' | Role ? {
19
+ [Answer in keyof Table[Key]]: NonEmpty<Table[Key][Answer]>;
20
+ } : never;
21
+ };
22
+ /** `'*'`, a field name, or `!name`. A row that names no list means `['*']`. */
23
+ export type FieldList<Names extends string = string> = '*' | Names | `!${Names}`;
24
+ /** What one role may do. Every key is optional, and a missing one denies. */
25
+ export type AccessRow<Paths extends string = string, Names extends string = string> = {
26
+ /** Only `true` allows: the runner denies any create answer that is not `true`. */
27
+ create?: boolean;
28
+ delete?: boolean | RowWhere<Paths>;
29
+ fields?: readonly FieldList<Names>[];
30
+ /**
31
+ * The catalog keys this role holds, `${kayan}.${operation}` or `${kayan}.fields.${field}`, or `'*'`.
32
+ * Read only where the build declares no access target; a key outside the catalog fails the build.
33
+ */
34
+ permissions?: '*' | readonly string[];
35
+ read?: boolean | RowWhere<Paths>;
36
+ update?: boolean | RowWhere<Paths>;
37
+ };
38
+ export type RowTable<Names extends string = string> = Record<string, AccessRow<string, Names>>;
39
+ export type RowsOptions<Actor = unknown> = {
40
+ /** What the build's access target resolved this actor holds; where it answers, it decides. */
41
+ holds?: (user: Actor) => Held;
42
+ /** Where an actor's roles are read from. Defaults to `user.roles`. */
43
+ roles?: (user: Actor) => readonly string[];
44
+ };
45
+ /** What an actor holds: `true` for every key, a set of keys, or nothing where no store grades and the row answers decide alone. */
46
+ export type Held = true | ReadonlySet<string> | undefined;
47
+ /** What `rows()` returns: the floor's access object, and the rows it came from. */
48
+ export type Rows<Table extends RowTable = RowTable> = {
49
+ create: Access;
50
+ delete: Access;
51
+ read: Access;
52
+ update: Access;
53
+ /** The three callbacks the field named `name` gets from these rows; without `kayan` a grading table denies the field. */
54
+ fields(name: string, kayan?: string): {
55
+ create: FieldAccess;
56
+ read: FieldAccess;
57
+ update: FieldAccess;
58
+ };
59
+ /** The keys this kayan's own rows hold for the actor. */
60
+ permissions(user: unknown): Held;
61
+ /** Which of these rows applied to an actor, so an error can name them. */
62
+ roles(user: unknown): string[];
63
+ /** The rows as written, so a tool can read them without running a callback. */
64
+ rows: Table;
65
+ };
66
+ /** The rows behind an access object, or nothing for hand-written callbacks. */
67
+ export declare const rowsOf: (access: unknown) => Rows | undefined;
68
+ /** The rows behind a field callback, or nothing for a hand-written one. */
69
+ export declare const rowsOfField: (access: unknown) => Rows | undefined;
70
+ export declare const declaredFieldAccess: (access: unknown) => unknown;
71
+ export declare const admissions: (fields: readonly string[] | undefined) => readonly string[];
72
+ /**
73
+ * Compiles a row per role onto the floor's callbacks: no applicable row denies, a `false` in any denies,
74
+ * a `true` with no `false` allows, and several filters admit a row any one of them admits.
75
+ */
76
+ export declare const rows: <const Table extends RowTable>(table: Table & CheckedTable<Table>, options?: RowsOptions) => Rows<Table>;
77
+ /** The access object a kayan gets from its rows, with the callbacks it declared beside them ANDed on. */
78
+ export declare const applyRows: (compiled: Rows, declared?: Record<string, unknown>) => Record<string, unknown>;
79
+ export declare const rowFields: ({ compiled, name, declared, kayan }: {
80
+ compiled: Rows;
81
+ name: string;
82
+ declared?: Record<string, unknown>;
83
+ kayan?: string;
84
+ }) => {
85
+ [k: string]: FieldAccess;
86
+ };
87
+ export {};
@@ -0,0 +1,174 @@
1
+ import { isRef, refuseLiteral } from './user.js';
2
+ import { combineWhere } from './where.js';
3
+ // A marker, not a security boundary. A symbol, so no field name collides with it
4
+ // and an object spread still carries it.
5
+ const ROWS = Symbol('mithaq.rows');
6
+ const rowFieldAccess = new WeakMap();
7
+ /** The rows behind an access object, or nothing for hand-written callbacks. */
8
+ export const rowsOf = (access) => access && typeof access === 'object' ? access[ROWS] : undefined;
9
+ /** The rows behind a field callback, or nothing for a hand-written one. */
10
+ export const rowsOfField = (access) => typeof access === 'function' ? rowFieldAccess.get(access) : undefined;
11
+ const narrowedFrom = new WeakMap();
12
+ // The narrowed callback never admits more than the declared one, so a check that
13
+ // a field kept a shipped callback reads the declared one here.
14
+ export const declaredFieldAccess = (access) => {
15
+ while (typeof access === 'function' && narrowedFrom.has(access))
16
+ access = narrowedFrom.get(access);
17
+ return access;
18
+ };
19
+ // A row naming nothing admits every field, and a list of only exclusions means every
20
+ // other field. Shared with `buildAsas`, so a build and a request never read one list two ways.
21
+ export const admissions = (fields) => {
22
+ const list = fields ?? ['*'];
23
+ return list.some((entry) => !entry.startsWith('!')) ? list : ['*', ...list];
24
+ };
25
+ const deepFreeze = (value, seen = new WeakSet()) => {
26
+ if (!value || typeof value !== 'object' || seen.has(value))
27
+ return;
28
+ seen.add(value);
29
+ Object.freeze(value);
30
+ for (const nested of Object.values(value))
31
+ deepFreeze(nested, seen);
32
+ };
33
+ const resolveValue = (value, user) => {
34
+ if (Array.isArray(value))
35
+ return value.map((one) => resolveValue(one, user));
36
+ refuseLiteral(value, 'An access row filter');
37
+ if (!isRef(value))
38
+ return value;
39
+ // An absent or `null` key throws: `{ organization: { equals: null } }` would read every
40
+ // row with no tenant. An own key only, since a JSON table could name `__proto__`.
41
+ const actor = user;
42
+ const found = actor && Object.hasOwn(actor, value.$user) ? actor[value.$user] : undefined;
43
+ if (found === null || found === undefined) {
44
+ throw new Error(`Access row filter "user.${value.$user}" found nothing on the actor, so there is no constraint to apply.`);
45
+ }
46
+ return found;
47
+ };
48
+ const resolveWhere = (where, user) => Object.fromEntries(Object.entries(where).map(([key, value]) => [
49
+ key,
50
+ key === 'and' || key === 'or'
51
+ ? value.map((one) => resolveWhere(one, user))
52
+ : Object.fromEntries(Object.entries(value).map(([operator, operand]) => [
53
+ operator,
54
+ resolveValue(operand, user),
55
+ ])),
56
+ ]));
57
+ /** Both answers applied, so a callback beside rows may narrow and never widen. */
58
+ const and = (left, right) => async (args) => {
59
+ const first = await left(args);
60
+ if (!first)
61
+ return false;
62
+ const second = await right(args);
63
+ if (!second)
64
+ return false;
65
+ if (first === true)
66
+ return second;
67
+ if (second === true)
68
+ return first;
69
+ return combineWhere(first, second);
70
+ };
71
+ const andField = (left, right) => async (args) => !!(await left(args)) && !!(await right(args));
72
+ const operations = ['create', 'delete', 'read', 'update'];
73
+ /**
74
+ * Compiles a row per role onto the floor's callbacks: no applicable row denies, a `false` in any denies,
75
+ * a `true` with no `false` allows, and several filters admit a row any one of them admits.
76
+ */
77
+ export const rows = (table, options) => {
78
+ // Frozen, nested objects too: a tool handed the table cannot rewrite what the
79
+ // callbacks answer, and a row's `Where` is read live each time.
80
+ deepFreeze(table);
81
+ const written = table;
82
+ const roles = (user) => {
83
+ if (!user)
84
+ return [];
85
+ const found = options?.roles ? options.roles(user) : user.roles;
86
+ return Array.isArray(found) ? found.filter((role) => typeof role === 'string') : [];
87
+ };
88
+ // An actor with no role takes no row, `'*'` included, so a `Where` naming a key an
89
+ // anonymous caller does not carry is never resolved.
90
+ const applicable = (user) => {
91
+ const assigned = roles(user);
92
+ if (!assigned.length)
93
+ return [];
94
+ const every = written['*'];
95
+ // An own key only: `constructor` read through the prototype would hand back `Object` as a row.
96
+ const own = assigned
97
+ .map((role) => (Object.hasOwn(written, role) ? written[role] : undefined))
98
+ .filter((row) => !!row);
99
+ return every ? [every, ...own] : own;
100
+ };
101
+ const answer = (operation) => ({ user }) => {
102
+ const hits = applicable(user).map((row) => row[operation] ?? false);
103
+ if (!hits.length || hits.some((hit) => hit === false))
104
+ return false;
105
+ if (hits.some((hit) => hit === true))
106
+ return true;
107
+ const filters = hits.map((one) => resolveWhere(one, user));
108
+ return filters.length === 1 ? filters[0] : { or: filters };
109
+ };
110
+ const admits = (name, user) => {
111
+ const lists = applicable(user).map((row) => admissions(row.fields));
112
+ return (lists.length > 0 &&
113
+ lists.every((list) => list.includes('*') || list.includes(name)) &&
114
+ !lists.some((list) => list.includes(`!${name}`)));
115
+ };
116
+ // A table grades only when some row names `permissions`; otherwise row answers decide.
117
+ const grades = Object.values(written).some((row) => row.permissions !== undefined);
118
+ const held = (user) => {
119
+ if (!grades)
120
+ return undefined;
121
+ const hits = applicable(user);
122
+ if (hits.some((row) => row.permissions === '*'))
123
+ return true;
124
+ // Several roles union. A row that names no list contributes nothing rather than everything.
125
+ return new Set(hits.flatMap((row) => (row.permissions === '*' ? [] : (row.permissions ?? []))));
126
+ };
127
+ const heldOf = (user) => options?.holds?.(user) ?? held(user);
128
+ const holdsField = (kayan, name, user) => {
129
+ const keys = heldOf(user);
130
+ if (keys === undefined || keys === true)
131
+ return true;
132
+ // The key it would need is one nothing can name.
133
+ return kayan !== undefined && keys.has(`${kayan}.fields.${name}`);
134
+ };
135
+ const compiled = {
136
+ create: answer('create'),
137
+ delete: answer('delete'),
138
+ read: answer('read'),
139
+ update: answer('update'),
140
+ fields: (name, kayan) => {
141
+ const admit = ({ user }) => admits(name, user) && holdsField(kayan, name, user);
142
+ rowFieldAccess.set(admit, compiled);
143
+ return { create: admit, read: admit, update: admit };
144
+ },
145
+ permissions: held,
146
+ roles,
147
+ rows: table,
148
+ };
149
+ return Object.assign(compiled, { [ROWS]: compiled });
150
+ };
151
+ /** The access object a kayan gets from its rows, with the callbacks it declared beside them ANDed on. */
152
+ export const applyRows = (compiled, declared) => {
153
+ const merged = { ...declared, [ROWS]: compiled, rows: compiled.rows };
154
+ delete merged.tenant;
155
+ for (const operation of operations) {
156
+ const own = declared?.[operation];
157
+ merged[operation] = own && own !== compiled[operation] ? and(compiled[operation], own) : compiled[operation];
158
+ }
159
+ return merged;
160
+ };
161
+ // Marked as coming from rows, so a write the field's own callback denies throws the
162
+ // naming error rather than dropping silently.
163
+ export const rowFields = ({ compiled, name, declared, kayan }) => {
164
+ const admit = compiled.fields(name, kayan);
165
+ return Object.fromEntries(Object.entries(admit).map(([operation, callback]) => {
166
+ const own = declared?.[operation];
167
+ if (!own)
168
+ return [operation, callback];
169
+ const narrowed = andField(callback, own);
170
+ rowFieldAccess.set(narrowed, compiled);
171
+ narrowedFrom.set(narrowed, own);
172
+ return [operation, narrowed];
173
+ }));
174
+ };
@@ -0,0 +1,72 @@
1
+ import type { Kind } from '../base/kinds.ts';
2
+ import type { Rule } from '../base/rules.ts';
3
+ import type { RuntimeTarget } from './language.ts';
4
+ export type Rukn = {
5
+ readonly name: string;
6
+ readonly kinds?: Readonly<Record<string, Kind>>;
7
+ readonly rules?: Readonly<Record<string, Rule>>;
8
+ readonly targets?: Readonly<Record<string, RuntimeTarget>>;
9
+ readonly needs?: readonly string[];
10
+ };
11
+ /** Declares a rukn, one pillar of capability (kinds, rules, targets and what it needs), keeping its literal types. */
12
+ export declare const defineRukn: <const R extends Rukn>(rukn: R) => R;
13
+ /** What a kayan writes under one of the build's targets, by that target's `type`; each target's folder augments it. */
14
+ export interface KayanConfigs<L, F, T> {
15
+ }
16
+ /** What a kayan writes under target `T` of language `L`, by `T`'s type. */
17
+ export type KayanConfigOf<L, F, T> = T extends {
18
+ readonly type: infer Type;
19
+ } ? Type extends keyof KayanConfigs<L, F, T> ? KayanConfigs<L, F, T>[Type] : unknown : unknown;
20
+ type U2I<U> = (U extends unknown ? (u: U) => void : never) extends (i: infer I) => void ? I : never;
21
+ type Index<A> = keyof A & `${number}`;
22
+ type Brought<R, K extends 'kinds' | 'rules' | 'targets'> = R extends {
23
+ readonly [P in K]: infer X extends object;
24
+ } ? X : {};
25
+ /** Every rukn's `K`, as one record. */
26
+ export type ArkanOf<A extends readonly Rukn[], K extends 'kinds' | 'rules' | 'targets'> = U2I<{
27
+ [I in Index<A>]: Brought<A[I], K>;
28
+ }[Index<A>]> & Record<never, never>;
29
+ /** The names of `K` that two arkan of `A` both bring. */
30
+ type Twice<A extends readonly Rukn[], K extends 'kinds' | 'rules' | 'targets'> = {
31
+ [I in Index<A>]: keyof Brought<A[I], K> & {
32
+ [J in Index<A>]: J extends I ? never : keyof Brought<A[J], K>;
33
+ }[Index<A>];
34
+ }[Index<A>] & string;
35
+ type CapabilitiesOf<Targets> = {
36
+ [Name in keyof Targets]: Targets[Name] extends {
37
+ readonly capability: infer Capability extends string;
38
+ } ? Capability : never;
39
+ }[keyof Targets];
40
+ export type ArkanRefusals<A extends readonly Rukn[], Targets> = {
41
+ [Name in Twice<A, 'kinds'> as `kind "${Name}" is claimed by two arkan`]: never;
42
+ } & {
43
+ [Name in Twice<A, 'rules'> as `rule "${Name}" is claimed by two arkan`]: never;
44
+ } & {
45
+ [Name in Twice<A, 'targets'> as `target "${Name}" is built by two arkan`]: never;
46
+ } & {
47
+ [Clash in {
48
+ [I in Index<A>]: `target "${keyof Brought<A[I], 'targets'> & keyof Targets & string}" is laid down by the project and built by rukn "${A[I]['name']}"`;
49
+ }[Index<A>]]: never;
50
+ } & {
51
+ [Need in {
52
+ [I in Index<A>]: A[I] extends {
53
+ readonly needs: readonly (infer N extends string)[];
54
+ } ? `rukn "${A[I]['name']}" needs a target of capability "${Exclude<N, CapabilitiesOf<Targets & ArkanOf<A, 'targets'>>>}"` : never;
55
+ }[Index<A>]]: never;
56
+ };
57
+ export declare const composeArkan: ({ arkan, targets }: {
58
+ arkan?: readonly Rukn[];
59
+ targets?: Readonly<Record<string, RuntimeTarget>>;
60
+ }) => {
61
+ kinds: Record<string, Kind>;
62
+ rules: Record<string, Rule>;
63
+ targets: Record<string, RuntimeTarget>;
64
+ };
65
+ export declare const implementationOf: <Built>({ table, shipped, name, own, type, }: {
66
+ table: Readonly<Record<string, Built>>;
67
+ shipped?: Readonly<Record<string, unknown>>;
68
+ name: string;
69
+ own: Readonly<Record<string, unknown>> | undefined;
70
+ type: string | undefined;
71
+ }) => Built | undefined;
72
+ export {};
@@ -0,0 +1,15 @@
1
+ /** Declares a rukn, one pillar of capability (kinds, rules, targets and what it needs), keeping its literal types. */
2
+ export const defineRukn = (rukn) => rukn;
3
+ // Refuses nothing, so a clash still reaches `buildAsas`, which names both sides.
4
+ export const composeArkan = ({ arkan = [], targets }) => ({
5
+ kinds: Object.assign({}, ...arkan.map((rukn) => rukn.kinds)),
6
+ rules: Object.assign({}, ...arkan.map((rukn) => rukn.rules)),
7
+ targets: Object.assign({}, ...arkan.map((rukn) => rukn.targets), targets),
8
+ });
9
+ // First match wins: the project's own entry in the target's table, then the kind's own, then the shipped one.
10
+ export const implementationOf = ({ table, shipped, name, own, type, }) => {
11
+ const entry = Object.hasOwn(table, name) ? table[name] : undefined;
12
+ if (entry !== undefined && entry !== shipped?.[name])
13
+ return entry;
14
+ return (type !== undefined && own && Object.hasOwn(own, type) ? own[type] : undefined) ?? entry;
15
+ };
@@ -0,0 +1,38 @@
1
+ /** Anything a schema target derives or a developer writes: one `~standard` member. */
2
+ export type StandardSchemaV1<Input = unknown, Output = Input> = {
3
+ readonly '~standard': StandardSchemaProps<Input, Output>;
4
+ };
5
+ /**
6
+ * What `~standard` carries: the version of the interface, the name of the
7
+ * library that made the schema, the one call that validates, and the two types
8
+ * the schema names for a reader, which exist only at compile time.
9
+ */
10
+ export type StandardSchemaProps<Input = unknown, Output = Input> = {
11
+ readonly version: 1;
12
+ readonly vendor: string;
13
+ readonly validate: (value: unknown, options?: {
14
+ readonly libraryOptions?: Record<string, unknown> | undefined;
15
+ }) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
16
+ readonly types?: {
17
+ readonly input: Input;
18
+ readonly output: Output;
19
+ } | undefined;
20
+ };
21
+ /** The value, or the reasons there is none. The absence of issues is success. */
22
+ export type StandardSchemaResult<Output> = {
23
+ readonly value: Output;
24
+ readonly issues?: undefined;
25
+ } | {
26
+ readonly issues: readonly StandardSchemaIssue[];
27
+ };
28
+ /** One reason: the message a caller reads, and where in the value it happened. */
29
+ export type StandardSchemaIssue = {
30
+ readonly message: string;
31
+ readonly path?: readonly (PropertyKey | {
32
+ readonly key: PropertyKey;
33
+ })[] | undefined;
34
+ };
35
+ /** The value a schema takes, before its own transforms run. */
36
+ export type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
37
+ /** The value a schema returns, after them. */
38
+ export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
@@ -0,0 +1,2 @@
1
+ // The Standard Schema v1 types, vendored: the spec package ships nothing but these.
2
+ export {};