@fabrika/app 0.8.2 → 0.9.0-cloudflare.2

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 (201) hide show
  1. package/package.json +40 -24
  2. package/skill/SKILL.md +29 -3
  3. package/skill/references/app-setup.md +29 -11
  4. package/skill/references/dev.md +2 -1
  5. package/skill/references/endpoint.md +12 -3
  6. package/skill/references/errors.md +39 -35
  7. package/skill/references/files.md +4 -2
  8. package/skill/references/jobs.md +9 -0
  9. package/skill/references/table-and-migration.md +19 -2
  10. package/skill/references/tests.md +33 -1
  11. package/skill/references/webhooks-links-connectors.md +1 -1
  12. package/src/build-recipe.ts +198 -0
  13. package/src/build.ts +60 -4
  14. package/src/bun.ts +33 -8
  15. package/src/cloudflare/application.ts +352 -0
  16. package/src/cloudflare/bindings.ts +159 -0
  17. package/src/cloudflare/names.ts +16 -0
  18. package/src/cloudflare/ops.ts +246 -0
  19. package/src/cloudflare/r2-files.ts +172 -0
  20. package/src/cloudflare.ts +99 -146
  21. package/src/contract/app-id.ts +5 -0
  22. package/src/contract/artifact-schema.ts +8 -1
  23. package/src/contract/build.ts +68 -11
  24. package/src/contract/compile.ts +1 -0
  25. package/src/contract/context.ts +9 -0
  26. package/src/contract/declaration.ts +2 -0
  27. package/src/contract/define.ts +28 -2
  28. package/src/contract/group.ts +10 -1
  29. package/src/contract/kit.ts +6 -2
  30. package/src/contract/platform.ts +3 -2
  31. package/src/contract/vocabulary.ts +26 -8
  32. package/src/dev/changes.ts +35 -0
  33. package/src/dev/contract-digest.ts +17 -0
  34. package/src/dev/index.ts +11 -0
  35. package/src/dev/launcher.ts +713 -0
  36. package/src/dev/output.ts +72 -0
  37. package/src/dev/wire.ts +136 -0
  38. package/src/disk-assets.ts +8 -3
  39. package/src/files/archive.ts +2 -2
  40. package/src/files/job-names.ts +12 -0
  41. package/src/files/kinds.ts +19 -7
  42. package/src/files/scan.ts +3 -4
  43. package/src/files/streams.ts +3 -1
  44. package/src/implement/anonymous.ts +11 -1
  45. package/src/implement/boot.ts +70 -7
  46. package/src/implement/bounded.ts +21 -2
  47. package/src/implement/capability.ts +18 -3
  48. package/src/implement/dispatch.ts +251 -147
  49. package/src/implement/egress.ts +341 -8
  50. package/src/implement/failures.ts +17 -1
  51. package/src/implement/faults.ts +1 -0
  52. package/src/implement/index.ts +1 -0
  53. package/src/implement/invocation.ts +18 -9
  54. package/src/implement/principal.ts +25 -2
  55. package/src/implement/terminals.ts +93 -0
  56. package/src/implement/workload.ts +24 -13
  57. package/src/jobs/report.ts +5 -1
  58. package/src/jobs/runner.ts +40 -19
  59. package/src/jobs/schedule.ts +7 -1
  60. package/src/jobs/scrub.ts +14 -1
  61. package/src/spa-fallback.ts +12 -0
  62. package/src/testing/endpoint-access.ts +307 -152
  63. package/src/testing/isolation.ts +8 -1
  64. package/src/testing/kit.ts +20 -2
  65. package/src/testing/proxy.ts +5 -5
  66. package/types/src/agent-skill.d.ts +29 -0
  67. package/types/src/build-recipe.d.ts +70 -0
  68. package/types/src/build.d.ts +29 -0
  69. package/types/src/bun.d.ts +66 -0
  70. package/types/src/client/caller.d.ts +19 -0
  71. package/types/src/client/contract.d.ts +131 -0
  72. package/types/src/client/error-reporting.d.ts +66 -0
  73. package/types/src/client/index.d.ts +4 -0
  74. package/types/src/client/rpc.d.ts +14 -0
  75. package/types/src/client/share.d.ts +2 -0
  76. package/types/src/client/transport.d.ts +42 -0
  77. package/types/src/cloudflare/application.d.ts +42 -0
  78. package/types/src/cloudflare/bindings.d.ts +33 -0
  79. package/types/src/cloudflare/names.d.ts +11 -0
  80. package/types/src/cloudflare/ops.d.ts +46 -0
  81. package/types/src/cloudflare/r2-files.d.ts +9 -0
  82. package/types/src/cloudflare.d.ts +42 -0
  83. package/types/src/contract/app-id.d.ts +2 -0
  84. package/types/src/contract/artifact-schema.d.ts +8 -0
  85. package/types/src/contract/attachment.d.ts +41 -0
  86. package/types/src/contract/build.d.ts +12 -0
  87. package/types/src/contract/compile.d.ts +11 -0
  88. package/types/src/contract/connect.d.ts +51 -0
  89. package/types/src/contract/context.d.ts +510 -0
  90. package/types/src/contract/declaration.d.ts +307 -0
  91. package/types/src/contract/defaults.d.ts +208 -0
  92. package/types/src/contract/define.d.ts +376 -0
  93. package/types/src/contract/file.d.ts +44 -0
  94. package/types/src/contract/group.d.ts +282 -0
  95. package/types/src/contract/handled.d.ts +18 -0
  96. package/types/src/contract/index.d.ts +19 -0
  97. package/types/src/contract/kit.d.ts +211 -0
  98. package/types/src/contract/platform.d.ts +9 -0
  99. package/types/src/contract/problems.d.ts +14 -0
  100. package/types/src/contract/redirect.d.ts +25 -0
  101. package/types/src/contract/schema-from-shape.d.ts +10 -0
  102. package/types/src/contract/schema-shape.d.ts +5 -0
  103. package/types/src/contract/snapshot-source.d.ts +15 -0
  104. package/types/src/contract/token.d.ts +58 -0
  105. package/types/src/contract/vocabulary.d.ts +32 -0
  106. package/types/src/contract/webhook-binding.d.ts +24 -0
  107. package/types/src/contract/webhook-scheme.d.ts +23 -0
  108. package/types/src/dev/changes.d.ts +7 -0
  109. package/types/src/dev/contract-digest.d.ts +1 -0
  110. package/types/src/dev/index.d.ts +2 -0
  111. package/types/src/dev/launcher.d.ts +32 -0
  112. package/types/src/dev/output.d.ts +28 -0
  113. package/types/src/dev/wire.d.ts +126 -0
  114. package/types/src/disk-assets.d.ts +6 -0
  115. package/types/src/errors.d.ts +46 -0
  116. package/types/src/fetch.d.ts +7 -0
  117. package/types/src/files/archive.d.ts +26 -0
  118. package/types/src/files/clamscan.d.ts +32 -0
  119. package/types/src/files/handle.d.ts +106 -0
  120. package/types/src/files/job-names.d.ts +9 -0
  121. package/types/src/files/kinds.d.ts +101 -0
  122. package/types/src/files/scan.d.ts +78 -0
  123. package/types/src/files/sniff.d.ts +7 -0
  124. package/types/src/files/streams.d.ts +17 -0
  125. package/types/src/files/treatment.d.ts +18 -0
  126. package/types/src/from-row.d.ts +20 -0
  127. package/types/src/honeypot.d.ts +2 -0
  128. package/types/src/implement/anonymous.d.ts +28 -0
  129. package/types/src/implement/attachment.d.ts +14 -0
  130. package/types/src/implement/boot.d.ts +120 -0
  131. package/types/src/implement/bounded.d.ts +32 -0
  132. package/types/src/implement/browser-config.d.ts +19 -0
  133. package/types/src/implement/capability.d.ts +144 -0
  134. package/types/src/implement/challenge.d.ts +75 -0
  135. package/types/src/implement/client-address.d.ts +11 -0
  136. package/types/src/implement/connect.d.ts +67 -0
  137. package/types/src/implement/crashes.d.ts +31 -0
  138. package/types/src/implement/dispatch.d.ts +221 -0
  139. package/types/src/implement/egress.d.ts +49 -0
  140. package/types/src/implement/environment.d.ts +14 -0
  141. package/types/src/implement/failures.d.ts +39 -0
  142. package/types/src/implement/faults.d.ts +45 -0
  143. package/types/src/implement/headers.d.ts +7 -0
  144. package/types/src/implement/implement.d.ts +58 -0
  145. package/types/src/implement/index.d.ts +25 -0
  146. package/types/src/implement/invocation.d.ts +125 -0
  147. package/types/src/implement/membership.d.ts +201 -0
  148. package/types/src/implement/multipart.d.ts +36 -0
  149. package/types/src/implement/params.d.ts +7 -0
  150. package/types/src/implement/principal.d.ts +57 -0
  151. package/types/src/implement/project.d.ts +11 -0
  152. package/types/src/implement/public.d.ts +61 -0
  153. package/types/src/implement/serve.d.ts +25 -0
  154. package/types/src/implement/terminals.d.ts +33 -0
  155. package/types/src/implement/tokens.d.ts +49 -0
  156. package/types/src/implement/types.d.ts +243 -0
  157. package/types/src/implement/urls.d.ts +15 -0
  158. package/types/src/implement/verifiers.d.ts +19 -0
  159. package/types/src/implement/webhook.d.ts +55 -0
  160. package/types/src/implement/workload.d.ts +56 -0
  161. package/types/src/implement/xlsx.d.ts +6 -0
  162. package/types/src/index.d.ts +12 -0
  163. package/types/src/jobs/exception.d.ts +40 -0
  164. package/types/src/jobs/mail.d.ts +67 -0
  165. package/types/src/jobs/report.d.ts +56 -0
  166. package/types/src/jobs/runner.d.ts +154 -0
  167. package/types/src/jobs/schedule.d.ts +17 -0
  168. package/types/src/jobs/scrub.d.ts +12 -0
  169. package/types/src/jobs/types.d.ts +98 -0
  170. package/types/src/react/caller.d.ts +30 -0
  171. package/types/src/react/errors.d.ts +40 -0
  172. package/types/src/react/hooks.d.ts +110 -0
  173. package/types/src/react/index.d.ts +5 -0
  174. package/types/src/react/provider.d.ts +58 -0
  175. package/types/src/react/store.d.ts +68 -0
  176. package/types/src/react/testing.d.ts +28 -0
  177. package/types/src/react/types.d.ts +33 -0
  178. package/types/src/rpc/builder.d.ts +52 -0
  179. package/types/src/rpc/contract.d.ts +23 -0
  180. package/types/src/rpc/dispatcher.d.ts +11 -0
  181. package/types/src/rpc/types.d.ts +62 -0
  182. package/types/src/service/define.d.ts +42 -0
  183. package/types/src/service/index.d.ts +3 -0
  184. package/types/src/service/serve.d.ts +28 -0
  185. package/types/src/spa-fallback.d.ts +2 -0
  186. package/types/src/standard-schema.d.ts +55 -0
  187. package/types/src/testing/caller.d.ts +95 -0
  188. package/types/src/testing/challenge.d.ts +11 -0
  189. package/types/src/testing/dev.d.ts +97 -0
  190. package/types/src/testing/endpoint-access.d.ts +131 -0
  191. package/types/src/testing/index.d.ts +12 -0
  192. package/types/src/testing/isolation.d.ts +3 -0
  193. package/types/src/testing/kit.d.ts +187 -0
  194. package/types/src/testing/membership.d.ts +132 -0
  195. package/types/src/testing/policy-isolation.d.ts +113 -0
  196. package/types/src/testing/preload.d.ts +1 -0
  197. package/types/src/testing/proxy.d.ts +40 -0
  198. package/types/src/testing/runner.d.ts +8 -0
  199. package/types/src/testing/sample-file.d.ts +5 -0
  200. package/types/src/testing/status.d.ts +4 -0
  201. package/types/src/undumpable.d.ts +5 -0
@@ -115,7 +115,16 @@ export type SignsField<Sg, Purposes extends string> =
115
115
  * `fetch` refuses any other while the site runs; a public procedure declares none.
116
116
  */
117
117
  export interface EgressField {
118
- readonly egress?: readonly `https://${string}`[]
118
+ readonly egress?: readonly `https://${string}`[] | Unavailable<"egress: 'public' is a cron job's only: record a row and kick a job that declares it">
119
+ }
120
+
121
+ /**
122
+ * A cron job's `egress`: its exact HTTPS origins, or `'public'`, every public host over HTTPS or HTTP on its default
123
+ * port, for a job that calls the websites its operators name (ADR-0070). The guard refuses IP addresses, local and
124
+ * internal names and the installation's own hosts, checks each redirect, and bounds each request's time and body.
125
+ */
126
+ export interface JobEgressField {
127
+ readonly egress?: readonly `https://${string}`[] | 'public'
119
128
  }
120
129
 
121
130
  /** Each purpose `signs` names is one of `defineApp`'s `tokens`. */
@@ -45,6 +45,7 @@ import {
45
45
  type Schema,
46
46
  type Surface,
47
47
  surfaceKit,
48
+ terminalOf,
48
49
  type WebhookMount,
49
50
  } from './define'
50
51
  import type {
@@ -56,6 +57,7 @@ import type {
56
57
  GroupBuild,
57
58
  GroupInput,
58
59
  GroupKit,
60
+ JobEgressField,
59
61
  KicksField,
60
62
  LimitsOption,
61
63
  LinkGroupKit,
@@ -73,13 +75,14 @@ type Owned<K extends string> = { readonly [P in K]?: never }
73
75
 
74
76
  /** The kit's types, computed once from the vocabulary, the tables' index, the imports and the token purposes. */
75
77
  export type KitOf<V, Ix, Imp extends KitImports, Tk extends TokenPurposes = Record<never, never>> = V extends
76
- AppVocabulary<infer A, infer D, infer R, infer Dims, infer M> ? {
78
+ AppVocabulary<infer A, infer D, infer R, infer Dims, infer M, infer T> ? {
77
79
  readonly action: A
78
80
  readonly dimension: D
79
81
  readonly creatable: CreatableDimension<Dims>
80
82
  readonly role: R
81
83
  readonly dimensions: Dims
82
84
  readonly members: M
85
+ readonly terminal: [T] extends [never] ? false : true
83
86
  readonly index: Ix
84
87
  readonly tables: Ix extends { readonly tables: infer T } ? keyof T & string : never
85
88
  readonly imports: Imp
@@ -94,7 +97,7 @@ export type CronOptions<Acts, S, U, Is, Ix extends AnyDbIndex, M = never, Ks = n
94
97
  & MailerField<M>
95
98
  & KicksField<Ks>
96
99
  & SignsField<Sg, Purposes>
97
- & EgressField
100
+ & JobEgressField
98
101
 
99
102
  /** The mail transport a job reaches, inferred from its optional field and then required, like a declared field. */
100
103
  type MailerField<M> =
@@ -555,6 +558,7 @@ function runtimeApp(vocabulary: AnyAppVocabulary, options: KitOptions | undefine
555
558
  roles: vocabulary.roles,
556
559
  ...presentationOf(vocabulary),
557
560
  ...peopleOf(vocabulary),
561
+ ...terminalOf(vocabulary),
558
562
  },
559
563
  dimensions: vocabulary.dimensions,
560
564
  surface: declared,
@@ -23,8 +23,8 @@ import {
23
23
  /**
24
24
  * The IAM vocabulary of a contract application, marked `contract` so IAM refuses every wildcard written for it: the
25
25
  * dimensions as scopes with their container, creator role and members actions; the actions; the roles, a membership
26
- * role with its scope and the roles that may assign it; the link mounts; the delegations; the workloads; and the locale,
27
- * display name and e-mail wording IAM names the application with. Every artifact
26
+ * role with its scope and the roles that may assign it; the link mounts; the delegations; the workloads; the locale,
27
+ * display name and e-mail wording IAM names the application with; and its terminal identification page. Every artifact
28
28
  * dimension is a scope, a file collection's `file:<name>` included, because access to a file is checked there.
29
29
  */
30
30
  export function schemaFromContract(artifact: ContractArtifact): AppSchema {
@@ -46,6 +46,7 @@ export function schemaFromContract(artifact: ContractArtifact): AppSchema {
46
46
  ...(artifact.mail === undefined ? {} : { mail: artifact.mail }),
47
47
  ...(artifact.members === undefined ? {} : { members: { manage: artifact.members.manage, list: artifact.members.list } }),
48
48
  ...(artifact.directory === 'labels' ? { directory: 'labels' } : {}),
49
+ ...(artifact.terminal === undefined ? {} : { terminal: { identify: artifact.terminal.identify } }),
49
50
  }
50
51
  }
51
52
 
@@ -2,20 +2,35 @@
2
2
  // nothing else, so the tables, the kit, the groups and the contract can each be typed by it without a cycle.
3
3
  // Browser-safe: plain data.
4
4
 
5
- import { type AppMembers, type DimensionDef, type Presentation, presentationOf, type RoleDefs, type Vocabulary, type WildcardMessage } from './define'
5
+ import {
6
+ type AppMembers,
7
+ type DimensionDef,
8
+ type Presentation,
9
+ presentationOf,
10
+ type RoleDefs,
11
+ type TerminalDeclaration,
12
+ type Vocabulary,
13
+ type WildcardMessage,
14
+ } from './define'
6
15
 
7
16
  /**
8
- * The vocabulary as `defineVocabulary` returned it, with its literal names, dimension definitions and the actions of its
9
- * people (`M`, `never` when it declares none).
17
+ * The vocabulary as `defineVocabulary` returned it, with its literal names, dimension definitions, the actions of its
18
+ * people (`M`, `never` when it declares none) and its terminal (`T`, `never` when it declares none).
10
19
  */
11
- export interface AppVocabulary<A extends string, D extends string, R extends string, Dims, M extends AppMembers<A> = never>
12
- extends Vocabulary<A, D, R>
13
- {
20
+ export interface AppVocabulary<
21
+ A extends string,
22
+ D extends string,
23
+ R extends string,
24
+ Dims,
25
+ M extends AppMembers<A> = never,
26
+ T extends TerminalDeclaration = never,
27
+ > extends Vocabulary<A, D, R> {
14
28
  readonly dimensions: Dims & { readonly [K in D]: DimensionDef<A, D, R> }
15
29
  readonly members?: M
30
+ readonly terminal?: T
16
31
  }
17
32
 
18
- export type AnyAppVocabulary = AppVocabulary<string, string, string, unknown, AppMembers<string>>
33
+ export type AnyAppVocabulary = AppVocabulary<string, string, string, unknown, AppMembers<string>, TerminalDeclaration>
19
34
 
20
35
  /**
21
36
  * Declares the dimensions, actions and roles, and how IAM names the application (`locale`, `displayName`). `src/vocabulary.ts` exports it; `defineTables`, `defineApp` and the
@@ -27,6 +42,7 @@ export function defineVocabulary<
27
42
  const R extends string,
28
43
  const Dims extends { readonly [K in D]: DimensionDef<NoInfer<A>, NoInfer<D>, NoInfer<R>> },
29
44
  const M extends AppMembers<NoInfer<A>> = never,
45
+ const T extends TerminalDeclaration = never,
30
46
  >(
31
47
  vocabulary: Presentation & {
32
48
  readonly app: string
@@ -35,8 +51,9 @@ export function defineVocabulary<
35
51
  readonly roles: RoleDefs<NoInfer<A>, NoInfer<D>, R>
36
52
  readonly members?: M
37
53
  readonly directory?: 'labels'
54
+ readonly terminal?: T
38
55
  },
39
- ): AppVocabulary<A, D, R, Dims, M> {
56
+ ): AppVocabulary<A, D, R, Dims, M, T> {
40
57
  const people = vocabulary.members
41
58
  return {
42
59
  app: vocabulary.app,
@@ -46,5 +63,6 @@ export function defineVocabulary<
46
63
  ...presentationOf(vocabulary),
47
64
  ...(people === undefined ? {} : { members: people }),
48
65
  ...(vocabulary.directory === undefined ? {} : { directory: vocabulary.directory }),
66
+ ...(vocabulary.terminal === undefined ? {} : { terminal: vocabulary.terminal }),
49
67
  }
50
68
  }
@@ -0,0 +1,35 @@
1
+ // What a sync changes, read off its paths: whether the build owns one (then the commit needs a bootstrap run), whether
2
+ // the migrations changed, and whether a web source did. Whether the contract changed is the digest's to say, not a path's.
3
+
4
+ import { CONTRACT_FILE } from '../build.js'
5
+
6
+ /** The application's migrations, as `fabrika db migrate` reads them. */
7
+ export const MIGRATIONS_DIRECTORY = 'db/migrations/'
8
+
9
+ /** Files whose change the build turns into something else: an install, or the generated run and build descriptors. */
10
+ const BUILD_OWNED_FILES: ReadonlySet<string> = new Set(['package.json', 'bun.lock', 'bun.lockb', 'bunfig.toml', 'zerops.yaml', 'zerops.yml'])
11
+ const BUILD_OWNED_DIRECTORIES = ['fabrika/', 'node_modules/'] as const
12
+
13
+ /** Server code and the database: a change only there leaves the SPA as it is. */
14
+ const SERVER_DIRECTORIES = ['src/', 'db/'] as const
15
+
16
+ /** Why the change needs a bootstrap run, in one line, or null when the launcher may apply it. */
17
+ export function bootstrapReason(paths: readonly string[]): string | null {
18
+ for (const path of paths) {
19
+ const why = BUILD_OWNED_FILES.has(path)
20
+ ? 'the dependencies, the lockfile or the build descriptor'
21
+ : BUILD_OWNED_DIRECTORIES.some((directory) => path.startsWith(directory))
22
+ ? 'the generated files or the installed packages'
23
+ : path !== CONTRACT_FILE && path.startsWith('fabrika.') && !path.includes('/')
24
+ ? 'a fabrika.* file'
25
+ : null
26
+ if (why !== null) return `${path} changed, which is ${why}; that needs a bootstrap run: call bind_dev again`
27
+ }
28
+ return null
29
+ }
30
+
31
+ export const migrationsChanged = (paths: readonly string[]): boolean => paths.some((path) => path.startsWith(MIGRATIONS_DIRECTORY))
32
+
33
+ /** A change outside the server code and the database may reach the SPA, so its build runs again. */
34
+ export const webSourceChanged = (paths: readonly string[]): boolean =>
35
+ paths.some((path) => !SERVER_DIRECTORIES.some((directory) => path.startsWith(directory)))
@@ -0,0 +1,17 @@
1
+ // The launcher's child that answers whether a commit changed the contract (ADR-0049). It runs in the application root
2
+ // with no credential in its environment, loads the application module the launcher itself never loads, and prints the
3
+ // digest the application's boot compares with `FABRIKA_CONTRACT_DIGEST`.
4
+
5
+ import { contractDigest } from '@fabrika/auth-core'
6
+ import { join } from 'node:path'
7
+ import { IMPLEMENTATION_FILE } from '../build.js'
8
+ import { compileContract } from '../contract/compile.js'
9
+ import { isContractImplementation } from '../implement/implement.js'
10
+
11
+ const module: unknown = await import(join(process.cwd(), IMPLEMENTATION_FILE))
12
+ const app = typeof module === 'object' && module !== null ? Reflect.get(module, 'default') : undefined
13
+ if (!isContractImplementation(app)) {
14
+ console.error(`${IMPLEMENTATION_FILE} does not default-export serve(contract, { … })`)
15
+ process.exit(1)
16
+ }
17
+ console.log(contractDigest(compileContract(app.contract, app.database?.tables ?? {})))
@@ -0,0 +1,11 @@
1
+ // @fabrika/app/dev — the launcher of a `dev` environment, which the generated dev entrypoint runs (Bun only).
2
+
3
+ export { bootstrapReason, migrationsChanged, webSourceChanged } from './changes.js'
4
+ export {
5
+ type DevLauncher,
6
+ type DevLauncherOptions,
7
+ type DevSourceTransport,
8
+ readDevLauncherConfig,
9
+ runDevLauncher,
10
+ startDevLauncher,
11
+ } from './launcher.js'