@cosmicdrift/kumiko-framework 0.386.0 → 0.387.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.
- package/LICENSE +21 -57
- package/README.md +2 -2
- package/agents/widgets.md +39 -39
- package/dist/api/api-constants.js +2 -2
- package/dist/api/auth-middleware.js +7 -7
- package/dist/api/auth-rate-limiters.js +7 -3
- package/dist/api/auth-routes-mfa.js +1 -1
- package/dist/api/auth-routes-session.js +1 -1
- package/dist/api/auth-routes-shared.js +7 -3
- package/dist/api/auth-routes-signup.js +1 -1
- package/dist/api/client-ip.js +3 -1
- package/dist/api/extra-route.js +1 -1
- package/dist/api/pii-leak-guard.js +4 -2
- package/dist/api/readiness.js +5 -5
- package/dist/api/redis-sse-broker.js +8 -6
- package/dist/api/routes.js +2 -2
- package/dist/api/server.js +34 -33
- package/dist/api/sse-broker.js +1 -1
- package/dist/api/sse-route.d.ts +13 -13
- package/dist/api/sse-route.js +15 -15
- package/dist/auth/index.js +4 -4
- package/dist/auth/roles.d.ts +4 -4
- package/dist/auth/roles.js +24 -24
- package/dist/compliance/duration-spec.js +18 -20
- package/dist/compliance/index.js +2 -3
- package/dist/compliance/override-schema.d.ts +4 -4
- package/dist/compliance/override-schema.js +19 -22
- package/dist/compliance/profiles.d.ts +26 -29
- package/dist/compliance/profiles.js +62 -69
- package/dist/compliance/sub-processors.d.ts +28 -30
- package/dist/compliance/sub-processors.js +17 -21
- package/dist/consumer-cli.js +4 -4
- package/dist/crypto/blind-index.js +18 -17
- package/dist/crypto/ciphertext-pattern.js +1 -1
- package/dist/crypto/event-pii.js +3 -3
- package/dist/crypto/kek-source.d.ts +1 -1
- package/dist/crypto/kek-source.js +3 -2
- package/dist/crypto/kms-adapter.d.ts +1 -1
- package/dist/crypto/kms-adapter.js +4 -6
- package/dist/crypto/kms-wiring.d.ts +1 -2
- package/dist/crypto/kms-wiring.js +2 -2
- package/dist/crypto/pg-kms-adapter.js +2 -6
- package/dist/crypto/pii-field-encryption.js +4 -4
- package/dist/crypto/subject-resolver.d.ts +1 -1
- package/dist/crypto/subject-resolver.js +2 -2
- package/dist/db/apply-entity-event.d.ts +6 -6
- package/dist/db/apply-entity-event.js +55 -55
- package/dist/db/blind-index-cleanup.js +7 -7
- package/dist/db/collect-table-metas.js +10 -11
- package/dist/db/compound-types.d.ts +1 -1
- package/dist/db/compound-types.js +12 -12
- package/dist/db/cursor.js +3 -4
- package/dist/db/dialect.d.ts +2 -2
- package/dist/db/dialect.js +2 -2
- package/dist/db/eagerload.d.ts +3 -3
- package/dist/db/eagerload.js +21 -12
- package/dist/db/entity-table-meta.d.ts +2 -2
- package/dist/db/entity-table-meta.js +4 -4
- package/dist/db/event-store-executor-context.d.ts +1 -1
- package/dist/db/event-store-executor-context.js +13 -13
- package/dist/db/event-store-executor-read.js +27 -28
- package/dist/db/event-store-executor-write.js +65 -70
- package/dist/db/located-timestamp.d.ts +11 -11
- package/dist/db/located-timestamp.js +29 -30
- package/dist/db/migrate-generator.d.ts +1 -1
- package/dist/db/migrate-generator.js +26 -19
- package/dist/db/migrate-runner.js +25 -25
- package/dist/db/money.d.ts +6 -6
- package/dist/db/money.js +29 -27
- package/dist/db/queries/backfill-pii.js +5 -5
- package/dist/db/queries/ddl.js +1 -1
- package/dist/db/queries/es-ops.d.ts +1 -1
- package/dist/db/queries/es-ops.js +1 -1
- package/dist/db/queries/event-consumer.js +3 -3
- package/dist/db/queries/event-store.js +4 -2
- package/dist/db/queries/projection-rebuild.js +3 -3
- package/dist/db/queries/raw-sql.d.ts +2 -2
- package/dist/db/queries/raw-sql.js +2 -2
- package/dist/db/queries/shadow-swap.js +19 -19
- package/dist/db/query.d.ts +1 -1
- package/dist/db/query.js +6 -6
- package/dist/db/rebuild-marker.js +20 -21
- package/dist/db/replay-migration-sql.js +2 -2
- package/dist/db/schema-inspection.js +4 -5
- package/dist/db/table-builder.d.ts +1 -2
- package/dist/db/table-builder.js +82 -82
- package/dist/db/tenant-db.d.ts +2 -2
- package/dist/db/tenant-db.js +9 -2
- package/dist/derivatives/delete-stored-file.js +5 -2
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/access-roles.js +4 -3
- package/dist/engine/boot-validator/api-ext.js +14 -14
- package/dist/engine/boot-validator/config-deps.js +7 -6
- package/dist/engine/boot-validator/detail-screens.js +3 -3
- package/dist/engine/boot-validator/entity-handler.js +60 -61
- package/dist/engine/boot-validator/gdpr-storage.js +4 -4
- package/dist/engine/boot-validator/i18n-keys.js +1 -1
- package/dist/engine/boot-validator/index.d.ts +2 -2
- package/dist/engine/boot-validator/index.js +20 -20
- package/dist/engine/boot-validator/nav.js +5 -5
- package/dist/engine/boot-validator/ownership.js +12 -9
- package/dist/engine/boot-validator/parent-ref.js +1 -1
- package/dist/engine/boot-validator/pii-retention.js +50 -57
- package/dist/engine/boot-validator/projection-list-screens.js +7 -7
- package/dist/engine/boot-validator/query-refs.js +1 -1
- package/dist/engine/boot-validator/screens-actions.js +15 -16
- package/dist/engine/boot-validator/screens-forms.js +10 -11
- package/dist/engine/boot-validator/screens-lists.js +14 -15
- package/dist/engine/boot-validator/screens.js +34 -40
- package/dist/engine/boot-validator/security-baseline.js +3 -2
- package/dist/engine/boot-validator/transfer-graph.js +2 -2
- package/dist/engine/boot-validator/workspaces.js +3 -3
- package/dist/engine/bridge-stub.js +3 -4
- package/dist/engine/build-app-schema.js +75 -75
- package/dist/engine/build-config-feature-schema.js +50 -50
- package/dist/engine/build-target.js +20 -23
- package/dist/engine/codemod/pipeline-codemod.js +20 -13
- package/dist/engine/config-helpers.js +4 -4
- package/dist/engine/constants.js +2 -2
- package/dist/engine/create-app.js +2 -2
- package/dist/engine/define-feature.js +12 -11
- package/dist/engine/define-handler.js +12 -11
- package/dist/engine/define-step.js +1 -1
- package/dist/engine/define-workflow.d.ts +5 -5
- package/dist/engine/define-workflow.js +6 -6
- package/dist/engine/entity-handlers.js +13 -14
- package/dist/engine/event-def.js +2 -2
- package/dist/engine/extension-names.d.ts +16 -17
- package/dist/engine/extension-names.js +37 -38
- package/dist/engine/extensions/escape-hatch-usage.js +1 -1
- package/dist/engine/extensions/tenant-data.js +2 -2
- package/dist/engine/extensions/user-data.d.ts +23 -24
- package/dist/engine/factories.d.ts +14 -13
- package/dist/engine/factories.js +38 -37
- package/dist/engine/feature-ast/entity-field-types.js +1 -1
- package/dist/engine/feature-ast/extractors/handlers.js +1 -1
- package/dist/engine/feature-ast/extractors/hooks.js +1 -1
- package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
- package/dist/engine/feature-ast/extractors/shared.js +2 -3
- package/dist/engine/feature-ast/parse.js +13 -13
- package/dist/engine/feature-ast/patch.js +1 -1
- package/dist/engine/feature-ast/pattern-change-schema.js +1 -1
- package/dist/engine/feature-ast/patterns.d.ts +2 -2
- package/dist/engine/feature-ast/render.js +2 -2
- package/dist/engine/feature-config-events-jobs.js +15 -12
- package/dist/engine/feature-entity-handlers.js +9 -5
- package/dist/engine/feature-manifest.d.ts +9 -9
- package/dist/engine/feature-manifest.js +6 -7
- package/dist/engine/feature-ui-extensions.d.ts +3 -3
- package/dist/engine/feature-ui-extensions.js +30 -22
- package/dist/engine/field-access.js +5 -6
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +4 -4
- package/dist/engine/object-form.d.ts +1 -0
- package/dist/engine/object-form.js +14 -4
- package/dist/engine/ownership.js +3 -3
- package/dist/engine/parse-ref-target.js +5 -7
- package/dist/engine/pattern-library/index.js +2 -2
- package/dist/engine/pattern-library/library.js +2 -2
- package/dist/engine/pattern-library/types.js +3 -3
- package/dist/engine/pipeline.js +2 -2
- package/dist/engine/project-app-schema-for-roles.js +1 -1
- package/dist/engine/projection-helpers.js +9 -9
- package/dist/engine/qualified-name.js +2 -2
- package/dist/engine/registry-facade.js +1 -1
- package/dist/engine/registry-ingest.js +12 -12
- package/dist/engine/registry-state.js +17 -19
- package/dist/engine/registry-validate.js +12 -12
- package/dist/engine/run-in.js +2 -2
- package/dist/engine/run-pipeline.js +7 -0
- package/dist/engine/schema-builder.js +40 -38
- package/dist/engine/screen-filter-ops.js +17 -20
- package/dist/engine/screen-helpers.js +4 -3
- package/dist/engine/state-machine.d.ts +12 -12
- package/dist/engine/state-machine.js +7 -7
- package/dist/engine/steps/_resolver-utils.js +4 -3
- package/dist/engine/steps/for-each.js +4 -4
- package/dist/engine/steps/unsafe-projection-delete.js +1 -1
- package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
- package/dist/engine/steps/wait-for-event.js +4 -4
- package/dist/engine/steps/workflow-system-events.js +3 -3
- package/dist/engine/tier-resolver-extension.d.ts +29 -27
- package/dist/engine/tier-resolver-extension.js +26 -26
- package/dist/engine/transfer-adjacency.js +2 -2
- package/dist/engine/validate-projection-allowlist.js +7 -27
- package/dist/engine/walk-step-instances.d.ts +2 -0
- package/dist/engine/walk-step-instances.js +38 -0
- package/dist/engine/where-rule-lint.js +1 -1
- package/dist/engine/write-handler-qn-extract.js +1 -1
- package/dist/entrypoint/index.js +3 -3
- package/dist/env/index.d.ts +7 -6
- package/dist/env/index.js +10 -9
- package/dist/errors/transition-details.js +7 -7
- package/dist/errors/write-error-info.d.ts +6 -6
- package/dist/errors/write-error-info.js +6 -6
- package/dist/es-ops/context.d.ts +9 -9
- package/dist/es-ops/context.js +28 -28
- package/dist/es-ops/index.js +7 -9
- package/dist/es-ops/operations-schema.js +4 -10
- package/dist/es-ops/runner.d.ts +11 -11
- package/dist/es-ops/runner.js +49 -53
- package/dist/es-ops/types.d.ts +55 -55
- package/dist/es-ops/types.js +11 -11
- package/dist/event-store/admin-api.js +6 -7
- package/dist/event-store/errors.js +1 -1
- package/dist/event-store/event-store.js +2 -2
- package/dist/event-store/rebuild-dead-letter.js +1 -1
- package/dist/files/feature.js +9 -10
- package/dist/files/file-handle.js +5 -6
- package/dist/files/file-ref-entity.js +15 -15
- package/dist/files/file-ref-table.js +7 -8
- package/dist/files/file-routes.d.ts +7 -0
- package/dist/files/file-routes.js +11 -5
- package/dist/files/in-memory-provider.js +8 -9
- package/dist/files/local-provider.js +18 -25
- package/dist/files/provider-resolver.d.ts +7 -7
- package/dist/files/storage-tracking.js +2 -2
- package/dist/files/zip-stream.d.ts +12 -19
- package/dist/files/zip-stream.js +44 -63
- package/dist/i18n/required-surface-keys.d.ts +1 -1
- package/dist/i18n/required-surface-keys.js +8 -8
- package/dist/jobs/job-runner-helpers.js +8 -2
- package/dist/jobs/job-runner-schedulers.d.ts +1 -1
- package/dist/jobs/job-runner-schedulers.js +3 -4
- package/dist/jobs/job-runner.js +13 -15
- package/dist/lifecycle/lifecycle.js +3 -5
- package/dist/lifecycle/signal-handlers.js +5 -4
- package/dist/logging/utils.d.ts +2 -1
- package/dist/logging/utils.js +38 -10
- package/dist/migrations/index.js +1 -1
- package/dist/migrations/kumiko-drift.js +14 -13
- package/dist/migrations/pending-rebuilds.d.ts +21 -20
- package/dist/migrations/pending-rebuilds.js +27 -27
- package/dist/migrations/projection-table-index.d.ts +3 -3
- package/dist/migrations/projection-table-index.js +8 -8
- package/dist/observability/metric-validator.js +1 -1
- package/dist/observability/metrics-handle.js +3 -3
- package/dist/pipeline/append-event-core.js +11 -2
- package/dist/pipeline/cascade-handler.d.ts +1 -1
- package/dist/pipeline/dispatch-batch.js +2 -2
- package/dist/pipeline/dispatch-query.js +10 -10
- package/dist/pipeline/dispatch-shared.d.ts +1 -0
- package/dist/pipeline/dispatch-shared.js +15 -17
- package/dist/pipeline/dispatch-stream.js +1 -1
- package/dist/pipeline/dispatch-write.js +3 -3
- package/dist/pipeline/dispatcher-utils.d.ts +1 -1
- package/dist/pipeline/dispatcher-utils.js +1 -1
- package/dist/pipeline/event-consumer-state.js +2 -2
- package/dist/pipeline/event-dispatcher-delivery.js +1 -1
- package/dist/pipeline/event-dispatcher.js +17 -28
- package/dist/pipeline/irreversible-operation-gate.js +1 -1
- package/dist/pipeline/lifecycle-pipeline.js +9 -9
- package/dist/pipeline/member-read-only-transaction.js +1 -3
- package/dist/pipeline/msp-rebuild.js +1 -1
- package/dist/pipeline/projection-rebuild.js +13 -12
- package/dist/pipeline/system-hooks.js +39 -41
- package/dist/pipeline/system-identity-switch.js +1 -3
- package/dist/pipeline/tenant-timezone-cache.js +1 -1
- package/dist/random/generate.d.ts +29 -29
- package/dist/random/generate.js +30 -30
- package/dist/random/words.js +20 -20
- package/dist/rate-limit/middleware.js +9 -5
- package/dist/redis/client.js +2 -2
- package/dist/redis/pubsub-signal.js +14 -17
- package/dist/schema-cli.js +116 -22
- package/dist/search/index.js +4 -4
- package/dist/search/purge-subject.js +4 -5
- package/dist/search/reindex-entity.js +6 -9
- package/dist/time/boot-tz-warning.d.ts +2 -2
- package/dist/time/boot-tz-warning.js +8 -8
- package/dist/time/iana.js +10 -10
- package/dist/time/index.js +9 -9
- package/dist/time/legacy-date.d.ts +2 -2
- package/dist/time/legacy-date.js +6 -8
- package/dist/time/polyfill.js +1 -1
- package/dist/time/tz-context.d.ts +1 -1
- package/dist/time/tz-context.js +1 -1
- package/dist/ui-types/app-schema.js +7 -7
- package/dist/ui-types/list-row-meta.js +4 -4
- package/dist/upgrade-cli.js +10 -9
- package/dist/utils/compare.js +1 -1
- package/package.json +4 -4
- package/src/changes.json +48 -0
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
- package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
- package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
- package/src/scripts/codemod/pii-personal-migration.ts +17 -8
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
import { getTemporal } from "../time/index.ts";
|
|
2
2
|
export interface ZipEntry {
|
|
3
|
-
/**
|
|
3
|
+
/** Path inside the ZIP, slash-separated and UTF-8 encoded (general-purpose flag bit 11). */
|
|
4
4
|
readonly path: string;
|
|
5
|
-
/** Body
|
|
5
|
+
/** Body as an AsyncIterable; it may be generated lazily to avoid upfront memory use. */
|
|
6
6
|
readonly data: AsyncIterable<Uint8Array>;
|
|
7
7
|
/**
|
|
8
|
-
* Optional
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Optional modification time. Defaults to the current time and uses the ZIP
|
|
9
|
+
* MS-DOS format with two-second precision. Set the generation timestamp on
|
|
10
|
+
* every entry when consistent audit-trail timestamps are required.
|
|
11
11
|
*/
|
|
12
12
|
readonly mtime?: InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
* `FileStorageProvider.writeStream
|
|
17
|
-
* Entry haengt im Memory bis fertig.
|
|
15
|
+
* Creates a ZIP stream as `AsyncIterable<Uint8Array>`. The caller, such as
|
|
16
|
+
* `FileStorageProvider.writeStream`, consumes the output chunk by chunk.
|
|
18
17
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* Trade-off: 1 Entry-Body im Memory at-a-time; bei <100 MB Files
|
|
25
|
-
* OK, bei >1 GB single-Files braeuchte ZIP64+Streaming-Descriptor).
|
|
26
|
-
* 2. File Body (raw bytes)
|
|
27
|
-
* Am Ende:
|
|
28
|
-
* 3. Central Directory (eine Liste-Entry pro File mit CRC + size)
|
|
29
|
-
* 4. EOCD (End Of Central Directory)
|
|
18
|
+
* Each entry's body is collected in memory before its local file header is
|
|
19
|
+
* emitted because non-seekable storage providers cannot use a streaming data
|
|
20
|
+
* descriptor, and the header must contain the CRC32 and size. This limits
|
|
21
|
+
* memory use to one entry body at a time; ZIP64 plus streaming descriptors
|
|
22
|
+
* would be needed for larger single files.
|
|
30
23
|
*/
|
|
31
24
|
export declare function createZipStream(entries: AsyncIterable<ZipEntry>): AsyncIterable<Uint8Array>;
|
package/dist/files/zip-stream.js
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
import { getTemporal } from "../time/index.js";
|
|
2
|
-
//
|
|
2
|
+
// Pure-JS streaming ZIP builder without dependencies.
|
|
3
3
|
//
|
|
4
|
-
// **Format
|
|
5
|
-
//
|
|
6
|
-
// -
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// - DEFLATE braucht zlib + buffering pro Entry, killt das Streaming-
|
|
10
|
-
// Property
|
|
4
|
+
// **Format: STORE (no DEFLATE).** Avoiding compression keeps the implementation minimal:
|
|
5
|
+
// - Storage providers (S3/R2) compress data on the wire (HTTP gzip).
|
|
6
|
+
// - JSON snippets are small, and file binaries are often already compressed
|
|
7
|
+
// (PNG/JPG/PDF/MP4/...).
|
|
8
|
+
// - DEFLATE requires zlib and per-entry buffering, which defeats streaming.
|
|
11
9
|
//
|
|
12
|
-
// **Streaming
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// ist. Storage-Provider's writeStream konsumiert direkt durch.
|
|
10
|
+
// **Streaming:** The caller provides `AsyncIterable<ZipEntry>`, and each entry
|
|
11
|
+
// has `data: AsyncIterable<Uint8Array>`. ZIP bytes are yielded chunk by chunk.
|
|
12
|
+
// The storage provider's writeStream consumes them directly.
|
|
16
13
|
//
|
|
17
|
-
// **ZIP
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// in unserem Use-Case: "profile.json", "files/<id>.<ext>").
|
|
14
|
+
// **ZIP standard:** APPNOTE.TXT v6.3.10 (April 2022). STORE method, no ZIP64
|
|
15
|
+
// (maximum 4 GB per entry and 65535 entries). ZIP64 support is needed before
|
|
16
|
+
// exporting a single file larger than 4 GB. The UTF-8 flag is enabled because
|
|
17
|
+
// filenames may contain non-ASCII characters.
|
|
22
18
|
//
|
|
23
|
-
// **CRC32:**
|
|
24
|
-
// einmal initialisiert + cached.
|
|
19
|
+
// **CRC32:** Classic IEEE-802.3 polynomial (0xEDB88320), with a cached lookup table.
|
|
25
20
|
const ZIP_VERSION_NEEDED = 20; // 2.0 = STORE
|
|
26
21
|
const ZIP_METHOD_STORE = 0;
|
|
27
22
|
// Local file header signature: 0x04034b50 = "PK\x03\x04"
|
|
@@ -31,50 +26,38 @@ const CENTRAL_DIR_HEADER_SIG = 0x02014b50;
|
|
|
31
26
|
// End of central directory record signature: 0x06054b50 = "PK\x05\x06"
|
|
32
27
|
const EOCD_SIG = 0x06054b50;
|
|
33
28
|
const VERSION_MADE_BY = 0x031e; // 0x03 = UNIX, 0x1e = ZIP 3.0
|
|
34
|
-
// General Purpose Flag Bit 11 (0x0800)
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// Umlauten ("Bügel.pdf") wird das Mojibake.
|
|
29
|
+
// General Purpose Flag Bit 11 (0x0800) enables UTF-8 encoding for filenames and comments.
|
|
30
|
+
// ASCII is valid UTF-8, while omitting the flag makes older ZIP tools interpret
|
|
31
|
+
// filenames as CP437, producing mojibake for non-ASCII characters such as umlauts.
|
|
38
32
|
const GENERAL_PURPOSE_FLAGS = 0x0800;
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// Bits entpackt Info-ZIP mit Mode 0000 (EACCES beim Read).
|
|
33
|
+
// Store UNIX file permissions in the high bytes of the external attributes.
|
|
34
|
+
// Without `(S_IFREG | 0644) << 16 = 0o100644 << 16 = 0x81a40000`, Info-ZIP
|
|
35
|
+
// extracts regular files with mode 0000, causing EACCES when they are read.
|
|
43
36
|
const UNIX_REGULAR_FILE_0644 = 0o100644 << 16; // 0x81a40000
|
|
44
|
-
//
|
|
45
|
-
// ZIP64
|
|
37
|
+
// STORE without ZIP64 is limited to 4 GB per entry and 65535 entries.
|
|
38
|
+
// ZIP64 support is required before these limits can be exceeded.
|
|
46
39
|
const ZIP_MAX_ENTRY_SIZE = 0xffffffff; // uint32 → 4 GB
|
|
47
40
|
const ZIP_MAX_ENTRIES = 0xffff; // uint16 → 65535
|
|
48
41
|
/**
|
|
49
|
-
*
|
|
50
|
-
* `FileStorageProvider.writeStream
|
|
51
|
-
* Entry haengt im Memory bis fertig.
|
|
42
|
+
* Creates a ZIP stream as `AsyncIterable<Uint8Array>`. The caller, such as
|
|
43
|
+
* `FileStorageProvider.writeStream`, consumes the output chunk by chunk.
|
|
52
44
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* Trade-off: 1 Entry-Body im Memory at-a-time; bei <100 MB Files
|
|
59
|
-
* OK, bei >1 GB single-Files braeuchte ZIP64+Streaming-Descriptor).
|
|
60
|
-
* 2. File Body (raw bytes)
|
|
61
|
-
* Am Ende:
|
|
62
|
-
* 3. Central Directory (eine Liste-Entry pro File mit CRC + size)
|
|
63
|
-
* 4. EOCD (End Of Central Directory)
|
|
45
|
+
* Each entry's body is collected in memory before its local file header is
|
|
46
|
+
* emitted because non-seekable storage providers cannot use a streaming data
|
|
47
|
+
* descriptor, and the header must contain the CRC32 and size. This limits
|
|
48
|
+
* memory use to one entry body at a time; ZIP64 plus streaming descriptors
|
|
49
|
+
* would be needed for larger single files.
|
|
64
50
|
*/
|
|
65
51
|
export async function* createZipStream(entries) {
|
|
66
52
|
const centralDirRecords = [];
|
|
67
|
-
let offset = 0; //
|
|
53
|
+
let offset = 0; // Tracks the local-header offset for each central-directory entry
|
|
68
54
|
for await (const entry of entries) {
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
// wollen vermeiden dass der konsumierende Storage zwei-pass laesst).
|
|
55
|
+
// Collect the body before writing the local file header: non-seekable storage
|
|
56
|
+
// providers cannot use a streaming data descriptor, and a two-pass read
|
|
57
|
+
// would be required otherwise.
|
|
73
58
|
const body = await collectBody(entry.data);
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
// ZIP produzieren (Decoder lesen Muell-Sizes). Lieber early-fail mit
|
|
77
|
-
// klarer Begruendung — Worker (Atom 3b) fängt das + setzt Job=failed.
|
|
59
|
+
// STORE without ZIP64 uses uint32 sizes. Fail early instead of allowing integer
|
|
60
|
+
// wraparound to produce a corrupt ZIP with incorrect sizes.
|
|
78
61
|
if (body.byteLength > ZIP_MAX_ENTRY_SIZE) {
|
|
79
62
|
throw new Error(`ZIP-Entry "${entry.path}" exceeds 4 GB limit (${body.byteLength} bytes). ` +
|
|
80
63
|
`STORE-mode without ZIP64-extension caps at uint32. ` +
|
|
@@ -105,8 +88,7 @@ export async function* createZipStream(entries) {
|
|
|
105
88
|
lfh.set(filenameBytes, 30);
|
|
106
89
|
yield lfh;
|
|
107
90
|
yield body;
|
|
108
|
-
//
|
|
109
|
-
// emittieren ihn am Ende.
|
|
91
|
+
// Keep central-directory entries until all file bodies have been emitted.
|
|
110
92
|
const cdh = new Uint8Array(46 + filenameBytes.byteLength);
|
|
111
93
|
const cdhView = new DataView(cdh.buffer);
|
|
112
94
|
cdhView.setUint32(0, CENTRAL_DIR_HEADER_SIG, true);
|
|
@@ -130,7 +112,7 @@ export async function* createZipStream(entries) {
|
|
|
130
112
|
centralDirRecords.push(cdh);
|
|
131
113
|
offset += lfh.byteLength + body.byteLength;
|
|
132
114
|
}
|
|
133
|
-
// Central
|
|
115
|
+
// Central directory: all entries back to back.
|
|
134
116
|
const centralDirStart = offset;
|
|
135
117
|
let centralDirSize = 0;
|
|
136
118
|
for (const record of centralDirRecords) {
|
|
@@ -189,15 +171,14 @@ function crc32(data) {
|
|
|
189
171
|
}
|
|
190
172
|
return (crc ^ 0xffffffff) >>> 0;
|
|
191
173
|
}
|
|
192
|
-
// MS-DOS
|
|
193
|
-
// Date: bits 9-15=year-1980, 5-8=month, 0-4=day.
|
|
194
|
-
// Time: bits 11-15=hour, 5-10=minute, 0-4=second/2.
|
|
174
|
+
// MS-DOS date and time encoding for ZIP headers.
|
|
175
|
+
// Date: bits 9-15 = year - 1980, 5-8 = month, 0-4 = day.
|
|
176
|
+
// Time: bits 11-15 = hour, 5-10 = minute, 0-4 = second / 2.
|
|
195
177
|
//
|
|
196
|
-
// **UTC**
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// server-side-Timestamps im Repo (Temporal.Instant ueberall).
|
|
178
|
+
// **UTC** instead of the local time zone: in a GDPR context the ZIP mtime is
|
|
179
|
+
// part of the data-access export. Different server time zones would otherwise produce
|
|
180
|
+
// different values for the same generation instant, causing audit drift. UTC is
|
|
181
|
+
// used for server-side timestamps throughout the repository.
|
|
201
182
|
function toDosTime(i) {
|
|
202
183
|
const dt = i.toZonedDateTimeISO("UTC");
|
|
203
184
|
const year = dt.year;
|
|
@@ -16,7 +16,7 @@ export declare function booleanFacetOptionKeys(featureName: string, entityName:
|
|
|
16
16
|
export declare function selectFacetOptionKey(featureName: string, entityName: string, fieldName: string, value: string): string;
|
|
17
17
|
export declare function screenTitleKey(screenId: string): string;
|
|
18
18
|
export type RequiredKeysOptions = {
|
|
19
|
-
/** Bypass `isI18nKey`'s colon-only check for generated dot-form labels
|
|
19
|
+
/** Bypass `isI18nKey`'s colon-only check for generated dot-form labels. */
|
|
20
20
|
readonly treatDotFormAsKey?: boolean;
|
|
21
21
|
};
|
|
22
22
|
export declare function requiredKeysFromScreen(featureName: string, screen: ScreenDefinition, options?: RequiredKeysOptions): readonly string[];
|
|
@@ -35,8 +35,8 @@ function isI18nKey(value) {
|
|
|
35
35
|
/**
|
|
36
36
|
* `treatAsKey` bypasses the colon-only `isI18nKey` check — used by the Settings-Hub
|
|
37
37
|
* generator's dot-form labels (`${feature}.settings`, mask titles), which are
|
|
38
|
-
* always i18n references by construction, never literal display text
|
|
39
|
-
* `isI18nKey` itself also accepts dot-form labels explicitly marked via `i18nKey()
|
|
38
|
+
* always i18n references by construction, never literal display text.
|
|
39
|
+
* `isI18nKey` itself also accepts dot-form labels explicitly marked via `i18nKey()`.
|
|
40
40
|
*/
|
|
41
41
|
function pushKey(out, value, treatAsKey = false) {
|
|
42
42
|
if (value !== undefined && (treatAsKey || isI18nKey(value)))
|
|
@@ -112,7 +112,7 @@ function pushDashboardScreenKeys(out, dashboard) {
|
|
|
112
112
|
pushRowActionKeys(out, action);
|
|
113
113
|
}
|
|
114
114
|
function pushDashboardPanelKeys(out, panel) {
|
|
115
|
-
// skip: custom
|
|
115
|
+
// skip: custom panels translate themselves, so no key is collected here
|
|
116
116
|
if (panel.kind === "custom")
|
|
117
117
|
return;
|
|
118
118
|
pushKey(out, panel.label);
|
|
@@ -251,7 +251,8 @@ export function requiredKeysFromScreen(featureName, screen, options = {}) {
|
|
|
251
251
|
}
|
|
252
252
|
case "projectionList": {
|
|
253
253
|
const list = screen;
|
|
254
|
-
//
|
|
254
|
+
// No entity means there are no field-label fallbacks; only explicit column labels are
|
|
255
|
+
// collected.
|
|
255
256
|
for (const col of list.columns) {
|
|
256
257
|
const normalized = normalizeListColumn(col);
|
|
257
258
|
if (normalized.label !== undefined)
|
|
@@ -454,9 +455,8 @@ function collectEntityListFilterKeys(feature, out) {
|
|
|
454
455
|
const entity = feature.entities?.[screen.entity];
|
|
455
456
|
if (!entity)
|
|
456
457
|
continue;
|
|
457
|
-
for (const [fieldName,
|
|
458
|
-
|
|
459
|
-
if (def.filterable !== true)
|
|
458
|
+
for (const [fieldName, def] of Object.entries(entity.fields)) {
|
|
459
|
+
if (!("filterable" in def) || def.filterable !== true)
|
|
460
460
|
continue;
|
|
461
461
|
if (def.type === "boolean") {
|
|
462
462
|
for (const key of booleanFacetOptionKeys(feature.name, screen.entity, fieldName)) {
|
|
@@ -513,7 +513,7 @@ export function buildEffectiveTranslationKeys(features) {
|
|
|
513
513
|
for (const key of Object.keys(feature.translations ?? {})) {
|
|
514
514
|
out.add(`${feature.name}:${key}`);
|
|
515
515
|
// Blank form too — Settings-Hub group namespaces (`group: "tenant-settings"`)
|
|
516
|
-
// require `${group}.settings` which is not `${feature}:${key}
|
|
516
|
+
// require `${group}.settings` which is not `${feature}:${key}`.
|
|
517
517
|
out.add(key);
|
|
518
518
|
}
|
|
519
519
|
}
|
|
@@ -66,6 +66,12 @@ export function serializeJobSubject(subject) {
|
|
|
66
66
|
.sort()
|
|
67
67
|
.map((field) => [field, subject[field] ?? null]));
|
|
68
68
|
}
|
|
69
|
+
function isJobSubjectValue(value) {
|
|
70
|
+
return (value === null ||
|
|
71
|
+
typeof value === "string" ||
|
|
72
|
+
typeof value === "number" ||
|
|
73
|
+
typeof value === "boolean");
|
|
74
|
+
}
|
|
69
75
|
// Stable identity for the declared payload fields: sorted keys, so two runs
|
|
70
76
|
// with the same subject values produce the same string and the later one
|
|
71
77
|
// replaces the earlier record. Non-primitives would serialize into something
|
|
@@ -76,7 +82,7 @@ export function jobSubjectKey(jobName, payload, fields, declaration) {
|
|
|
76
82
|
const subject = {};
|
|
77
83
|
for (const field of fields) {
|
|
78
84
|
const value = payload[field] ?? null;
|
|
79
|
-
if (value
|
|
85
|
+
if (!isJobSubjectValue(value)) {
|
|
80
86
|
throw new Error(`Job "${jobName}": ${declaration}.subjectFields["${field}"] must be a primitive, got ${typeof value}`);
|
|
81
87
|
}
|
|
82
88
|
subject[field] = value;
|
|
@@ -250,7 +256,7 @@ export function payloadMatchesTriggerFilter(where, payload) {
|
|
|
250
256
|
// on the children still existing in Redis when a wrapper retry re-derives
|
|
251
257
|
// their ids. The margin here is generous — queue wait time isn't bounded by
|
|
252
258
|
// backoff — to cover realistic windows. Audit trail lives in read_job_runs,
|
|
253
|
-
// not BullMQ
|
|
259
|
+
// not BullMQ.
|
|
254
260
|
export const COMPLETED_JOB_RETENTION_AGE_SEC = 86_400; // 24h
|
|
255
261
|
export const FAILED_JOB_RETENTION_AGE_SEC = 604_800; // 7d
|
|
256
262
|
// Total wall-clock time BullMQ can hold a perTenant wrapper across all its
|
|
@@ -7,7 +7,7 @@ export declare const ACTIVE_TENANT_IDS_QUERY_NAME = "tenant:query:active-tenant-
|
|
|
7
7
|
* BullMQ job ids are `repeat:<schedulerId>:<millis>`. Colons inside the
|
|
8
8
|
* scheduler id push the segment count to ≥5, which BullMQ's legacy heuristic
|
|
9
9
|
* treated as old repeatables — spawning a new scheduler entry every tick and
|
|
10
|
-
* leaking permanent `repeat:*` hashes (taskforcesh/bullmq#3828,
|
|
10
|
+
* leaking permanent `repeat:*` hashes (taskforcesh/bullmq#3828,
|
|
11
11
|
* publicstatus Redis OOM). Strip `.` and `:` from the job QN.
|
|
12
12
|
*/
|
|
13
13
|
export declare function schedulerIdForJobName(jobName: string): string;
|
|
@@ -19,13 +19,13 @@ export const ACTIVE_TENANT_IDS_QUERY_NAME = "tenant:query:active-tenant-ids";
|
|
|
19
19
|
* BullMQ job ids are `repeat:<schedulerId>:<millis>`. Colons inside the
|
|
20
20
|
* scheduler id push the segment count to ≥5, which BullMQ's legacy heuristic
|
|
21
21
|
* treated as old repeatables — spawning a new scheduler entry every tick and
|
|
22
|
-
* leaking permanent `repeat:*` hashes (taskforcesh/bullmq#3828,
|
|
22
|
+
* leaking permanent `repeat:*` hashes (taskforcesh/bullmq#3828,
|
|
23
23
|
* publicstatus Redis OOM). Strip `.` and `:` from the job QN.
|
|
24
24
|
*/
|
|
25
25
|
export function schedulerIdForJobName(jobName) {
|
|
26
26
|
return `scheduler-${jobName.replace(/[.:]/g, "-")}`;
|
|
27
27
|
}
|
|
28
|
-
// Same colon-in-BullMQ-id hazard as schedulerIdForJobName
|
|
28
|
+
// Same colon-in-BullMQ-id hazard as schedulerIdForJobName —
|
|
29
29
|
// a QN like "publicstatus:job:uptime-probe" must not leave ":" in the id.
|
|
30
30
|
export function bootJobIdForJobName(jobName) {
|
|
31
31
|
return `boot-${jobName.replace(/[.:]/g, "-")}`;
|
|
@@ -97,8 +97,7 @@ export async function removeOrphanJobSchedulers(queue, expectedSchedulerIds, lan
|
|
|
97
97
|
// duplicates — safe only as long as the child job hash outlives the
|
|
98
98
|
// wrapper's retry window, enforced at construction against
|
|
99
99
|
// COMPLETED_JOB_RETENTION_AGE_SEC (see the invariant check in
|
|
100
|
-
// createJobRunner).
|
|
101
|
-
// (fw#1603/#1604) — the wrapper id already contains ":", so strip
|
|
100
|
+
// createJobRunner). The wrapper id already contains ":", so strip
|
|
102
101
|
// separators from both halves before joining instead of interpolating raw.
|
|
103
102
|
export function perTenantChildJobId(wrapperJobId, tenantId) {
|
|
104
103
|
const sanitize = (value) => value.replace(/[.:]/g, "-");
|
package/dist/jobs/job-runner.js
CHANGED
|
@@ -139,8 +139,7 @@ export function createJobRunner(options) {
|
|
|
139
139
|
lockRedis = new Redis(redisOpts);
|
|
140
140
|
// Without a listener, a post-close 'error' (e.g. a teardown-race
|
|
141
141
|
// "Connection is closed") is unhandled and crashes the process — in
|
|
142
|
-
// bun:test it gets attributed to whichever test happens to run next
|
|
143
|
-
// (fw#1805).
|
|
142
|
+
// bun:test it gets attributed to whichever test happens to run next.
|
|
144
143
|
lockRedis.on("error", (err) => {
|
|
145
144
|
const log = stopping ? errorLogger.debug : errorLogger.error;
|
|
146
145
|
log("lock redis connection error", { error: err.message });
|
|
@@ -181,7 +180,7 @@ export function createJobRunner(options) {
|
|
|
181
180
|
}),
|
|
182
181
|
};
|
|
183
182
|
// Same unhandled-'error'-crash hazard as lockRedis above, just via
|
|
184
|
-
// BullMQ's internal ioredis client
|
|
183
|
+
// BullMQ's internal ioredis client.
|
|
185
184
|
for (const queue of Object.values(queues)) {
|
|
186
185
|
queue.on("error", (err) => {
|
|
187
186
|
const log = stopping ? errorLogger.debug : errorLogger.error;
|
|
@@ -557,7 +556,7 @@ export function createJobRunner(options) {
|
|
|
557
556
|
// Same buildHandlerContext parity as ctx.files above: ctx.notify/ctx.config
|
|
558
557
|
// must resolve for jobs the same way they do for write-handlers, or an
|
|
559
558
|
// app-author job calling ctx.notify(...)/ctx.config(...) hits a TypeError
|
|
560
|
-
// at runtime the write-handler path never would
|
|
559
|
+
// at runtime the write-handler path never would.
|
|
561
560
|
const notify = context._notifyFactory?.(jobSystemUser, tenantId, selfRunner, undefined, context._fileProviderResolver);
|
|
562
561
|
const configDb = context.db; // @cast-boundary db-operator
|
|
563
562
|
// Shared by the config accessor and ctx.derivatives below — both need the
|
|
@@ -739,7 +738,7 @@ export function createJobRunner(options) {
|
|
|
739
738
|
const handlerResult = await requestContext.run({
|
|
740
739
|
requestId: jobRequestId,
|
|
741
740
|
correlationId: jobCorrelationId,
|
|
742
|
-
//
|
|
741
|
+
// Events a job writes carry the job as their origin.
|
|
743
742
|
handler: jobName,
|
|
744
743
|
feature: registry.getJobFeature(jobName) ?? qnScope(jobName),
|
|
745
744
|
writeOrigin: jobOrigin,
|
|
@@ -794,7 +793,7 @@ export function createJobRunner(options) {
|
|
|
794
793
|
"job.id": jobId,
|
|
795
794
|
"job.attempt": attempt,
|
|
796
795
|
"kumiko.tenant_id": tenantId,
|
|
797
|
-
// Lane-routing attributes
|
|
796
|
+
// Lane-routing attributes. `run_in` is the job's
|
|
798
797
|
// declared lane (explicit or default-"worker"); `consumer_lane`
|
|
799
798
|
// is which runner actually executed it. They diverge in
|
|
800
799
|
// all-in-one (both lanes live in one process) but must match
|
|
@@ -840,7 +839,7 @@ export function createJobRunner(options) {
|
|
|
840
839
|
// Boot gates run inline and are awaited, so a throwing gate rejects start()
|
|
841
840
|
// — and with it the entrypoint's start() and runProdApp's boot, before the
|
|
842
841
|
// process reports ready. Enqueuing them (as runOnBoot does) would only fail
|
|
843
|
-
// a queue job and let the deploy through
|
|
842
|
+
// a queue job and let the deploy through.
|
|
844
843
|
async function runBootGates(lane) {
|
|
845
844
|
for (const [name, jobDef] of allJobs) {
|
|
846
845
|
if (laneForJob(jobDef) !== lane)
|
|
@@ -978,7 +977,7 @@ export function createJobRunner(options) {
|
|
|
978
977
|
// still-settling blocking connection: it rejects in-flight commands
|
|
979
978
|
// via ioredis's flushQueue() during close(), which isn't a listenable
|
|
980
979
|
// 'error' event — the only fix is to not return until both of the
|
|
981
|
-
// worker's connections (main + blocking) are ready
|
|
980
|
+
// worker's connections (main + blocking) are ready. This
|
|
982
981
|
// mirrors the wait BullMQ already does internally for
|
|
983
982
|
// upsertJobScheduler()/add() below when the lane has a cron/boot job.
|
|
984
983
|
// Racing a timeout against it keeps an unreachable Redis from hanging
|
|
@@ -1070,7 +1069,7 @@ export function createJobRunner(options) {
|
|
|
1070
1069
|
}
|
|
1071
1070
|
// BullMQ close() while a Queue's connection is still 'initializing'
|
|
1072
1071
|
// rejects the in-flight INFO with "Connection is closed." on an emitter
|
|
1073
|
-
// without listeners -> unhandled rejection
|
|
1072
|
+
// without listeners -> unhandled rejection. Wait for readiness
|
|
1074
1073
|
// first; a failed/timed-out wait must not block the close itself.
|
|
1075
1074
|
const stopReadyTimeoutMs = Math.min(bootRedisTimeoutMs, STOP_QUEUE_READY_TIMEOUT_MS);
|
|
1076
1075
|
await settleAbandonedBootGates(stopReadyTimeoutMs);
|
|
@@ -1203,8 +1202,8 @@ export function createJobRunner(options) {
|
|
|
1203
1202
|
for (const [name, jobDef] of allJobs) {
|
|
1204
1203
|
if (!("on" in jobDef.trigger))
|
|
1205
1204
|
continue;
|
|
1206
|
-
// skip:
|
|
1207
|
-
//
|
|
1205
|
+
// skip: other trigger types (cron, manual) do not respond to events.
|
|
1206
|
+
// Only "on" triggers are matched here.
|
|
1208
1207
|
const triggerOn = jobDef.trigger.on;
|
|
1209
1208
|
const matches = Array.isArray(triggerOn)
|
|
1210
1209
|
? triggerOn.includes(eventName)
|
|
@@ -1218,10 +1217,9 @@ export function createJobRunner(options) {
|
|
|
1218
1217
|
data["_tenantId"] = user.tenantId;
|
|
1219
1218
|
data["_triggeredById"] = user.id;
|
|
1220
1219
|
}
|
|
1221
|
-
// Multi-
|
|
1222
|
-
//
|
|
1223
|
-
//
|
|
1224
|
-
// (kein "ist es Multi?"-Branch nötig).
|
|
1220
|
+
// Multi-trigger: add _triggerName to the payload so the handler knows which
|
|
1221
|
+
// of the N triggers fired. Set it for single triggers too; this costs
|
|
1222
|
+
// nothing and keeps handler code simple (no "is it multi?" branch).
|
|
1225
1223
|
data["_triggerName"] = eventName;
|
|
1226
1224
|
if (triggerEvent)
|
|
1227
1225
|
data["_triggerEvent"] = triggerEvent;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Process lifecycle: 4-state machine + LIFO shutdown hooks.
|
|
2
2
|
// Signal wiring lives in signal-handlers.ts; v1 scope in architecture/lifecycle.md.
|
|
3
|
+
import { createFallbackLogger } from "../logging/utils.js";
|
|
3
4
|
const DEFAULT_DRAIN_TIMEOUT_MS = 40_000;
|
|
4
5
|
export function createLifecycle(opts = {}) {
|
|
5
6
|
const now = opts.now ?? (() => Date.now());
|
|
@@ -103,9 +104,6 @@ export function createLifecycle(opts = {}) {
|
|
|
103
104
|
};
|
|
104
105
|
}
|
|
105
106
|
function makeErrorLogger(logger) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
// biome-ignore lint/suspicious/noConsole: ops-visible fallback when no logger is wired
|
|
110
|
-
return (msg, err) => console.error(`[lifecycle] ${msg}:`, err);
|
|
107
|
+
const log = createFallbackLogger("lifecycle", logger);
|
|
108
|
+
return (msg, err) => log.error(msg, { err });
|
|
111
109
|
}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
export function attachSignalHandlers(lifecycle, opts = {}) {
|
|
5
5
|
const signals = opts.signals ?? ["SIGTERM", "SIGINT"];
|
|
6
6
|
const exitFn = opts.exit ?? ((code) => process.exit(code));
|
|
7
|
+
// bun-types vs @types/node: signal overload on process.on/off is not
|
|
8
|
+
// selectable via bind() under the merged types — EventEmitter seam is intentional.
|
|
9
|
+
const processEmitter = process;
|
|
7
10
|
const listeners = new Map();
|
|
8
11
|
// Guard so double-SIGTERM doesn't call exitFn twice. drain() is already
|
|
9
12
|
// idempotent via its shared promise, but its .then/.catch chain would fire
|
|
@@ -24,15 +27,13 @@ export function attachSignalHandlers(lifecycle, opts = {}) {
|
|
|
24
27
|
.then(() => exitFn(0))
|
|
25
28
|
.catch(() => exitFn(1));
|
|
26
29
|
};
|
|
27
|
-
|
|
28
|
-
// selectable via bind() under the merged types — EventEmitter seam is intentional.
|
|
29
|
-
process.on(sig, handler);
|
|
30
|
+
processEmitter.on(sig, handler);
|
|
30
31
|
listeners.set(sig, handler);
|
|
31
32
|
}
|
|
32
33
|
return {
|
|
33
34
|
detach: () => {
|
|
34
35
|
for (const [sig, handler] of listeners) {
|
|
35
|
-
|
|
36
|
+
processEmitter.off(sig, handler);
|
|
36
37
|
}
|
|
37
38
|
listeners.clear();
|
|
38
39
|
},
|
package/dist/logging/utils.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Logger } from "./types.ts";
|
|
2
2
|
type FallbackLogger = {
|
|
3
|
+
info(msg: string, data?: Record<string, unknown>): void;
|
|
3
4
|
error(msg: string, data?: Record<string, unknown>): void;
|
|
4
5
|
warn(msg: string, data?: Record<string, unknown>): void;
|
|
5
6
|
debug(msg: string, data?: Record<string, unknown>): void;
|
|
6
7
|
};
|
|
7
8
|
export declare function setProcessLogger(logger: Logger | null): void;
|
|
8
|
-
export declare function createFallbackLogger(namespace: string, logger?: (Pick<Logger, "error"> & Partial<Pick<Logger, "warn" | "debug">>) | undefined): FallbackLogger;
|
|
9
|
+
export declare function createFallbackLogger(namespace: string, logger?: (Pick<Logger, "error"> & Partial<Pick<Logger, "info" | "warn" | "debug" | "child">>) | undefined): FallbackLogger;
|
|
9
10
|
export {};
|
package/dist/logging/utils.js
CHANGED
|
@@ -3,9 +3,33 @@ let processLogger = null;
|
|
|
3
3
|
export function setProcessLogger(logger) {
|
|
4
4
|
processLogger = logger;
|
|
5
5
|
}
|
|
6
|
+
// Bun prints a trailing "undefined" when a second argument is passed explicitly as undefined.
|
|
7
|
+
function writeConsole(write, line, data) {
|
|
8
|
+
if (data === undefined)
|
|
9
|
+
write(line);
|
|
10
|
+
else
|
|
11
|
+
write(line, data);
|
|
12
|
+
}
|
|
6
13
|
export function createFallbackLogger(namespace, logger) {
|
|
14
|
+
if (logger?.child) {
|
|
15
|
+
const child = logger.child({ component: namespace });
|
|
16
|
+
return {
|
|
17
|
+
info: (msg, data) => child.info(msg, data),
|
|
18
|
+
error: (msg, data) => child.error(msg, data),
|
|
19
|
+
warn: (msg, data) => child.warn(msg, data),
|
|
20
|
+
debug: (msg, data) => child.debug(msg, data),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
7
23
|
if (logger) {
|
|
8
24
|
return {
|
|
25
|
+
info(msg, data) {
|
|
26
|
+
if (logger.info) {
|
|
27
|
+
logger.info(`[${namespace}] ${msg}`, data);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
writeConsole(console.info, `[${namespace}] ${msg}`, data);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
9
33
|
error(msg, data) {
|
|
10
34
|
logger.error(`[${namespace}] ${msg}`, data);
|
|
11
35
|
},
|
|
@@ -14,8 +38,7 @@ export function createFallbackLogger(namespace, logger) {
|
|
|
14
38
|
logger.warn(`[${namespace}] ${msg}`, data);
|
|
15
39
|
}
|
|
16
40
|
else {
|
|
17
|
-
|
|
18
|
-
console.warn(`[${namespace}] ${msg}`, data);
|
|
41
|
+
writeConsole(console.warn, `[${namespace}] ${msg}`, data);
|
|
19
42
|
}
|
|
20
43
|
},
|
|
21
44
|
debug(msg, data) {
|
|
@@ -23,8 +46,7 @@ export function createFallbackLogger(namespace, logger) {
|
|
|
23
46
|
logger.debug(`[${namespace}] ${msg}`, data);
|
|
24
47
|
}
|
|
25
48
|
else {
|
|
26
|
-
|
|
27
|
-
console.debug(`[${namespace}] ${msg}`, data);
|
|
49
|
+
writeConsole(console.debug, `[${namespace}] ${msg}`, data);
|
|
28
50
|
}
|
|
29
51
|
},
|
|
30
52
|
};
|
|
@@ -41,14 +63,22 @@ export function createFallbackLogger(namespace, logger) {
|
|
|
41
63
|
return cachedChild;
|
|
42
64
|
};
|
|
43
65
|
return {
|
|
66
|
+
info(msg, data) {
|
|
67
|
+
const child = resolveProcessChild();
|
|
68
|
+
if (child) {
|
|
69
|
+
child.info(msg, data);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
writeConsole(console.info, `[${namespace}] ${msg}`, data);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
44
75
|
error(msg, data) {
|
|
45
76
|
const child = resolveProcessChild();
|
|
46
77
|
if (child) {
|
|
47
78
|
child.error(msg, data);
|
|
48
79
|
}
|
|
49
80
|
else {
|
|
50
|
-
|
|
51
|
-
console.error(`[${namespace}] ${msg}`, data);
|
|
81
|
+
writeConsole(console.error, `[${namespace}] ${msg}`, data);
|
|
52
82
|
}
|
|
53
83
|
},
|
|
54
84
|
warn(msg, data) {
|
|
@@ -57,8 +87,7 @@ export function createFallbackLogger(namespace, logger) {
|
|
|
57
87
|
child.warn(msg, data);
|
|
58
88
|
}
|
|
59
89
|
else {
|
|
60
|
-
|
|
61
|
-
console.warn(`[${namespace}] ${msg}`, data);
|
|
90
|
+
writeConsole(console.warn, `[${namespace}] ${msg}`, data);
|
|
62
91
|
}
|
|
63
92
|
},
|
|
64
93
|
debug(msg, data) {
|
|
@@ -67,8 +96,7 @@ export function createFallbackLogger(namespace, logger) {
|
|
|
67
96
|
child.debug(msg, data);
|
|
68
97
|
}
|
|
69
98
|
else {
|
|
70
|
-
|
|
71
|
-
console.debug(`[${namespace}] ${msg}`, data);
|
|
99
|
+
writeConsole(console.debug, `[${namespace}] ${msg}`, data);
|
|
72
100
|
}
|
|
73
101
|
},
|
|
74
102
|
};
|
package/dist/migrations/index.js
CHANGED
|
@@ -4,5 +4,5 @@ export { assertKumikoSchemaCurrent, detectKumikoDrift, formatKumikoDriftReport,
|
|
|
4
4
|
export { createPendingRebuildsTable, enqueueProjectionRebuild, listPendingRebuilds, PROJECTION_REBUILD_JOB, pendingRebuildsTable, queueRebuildsFromMarkers, rebuildProjectionOrMultiStream, reportPendingRebuildRun, runPendingRebuilds, } from "./pending-rebuilds.js";
|
|
5
5
|
// Boot-time backfill: re-encrypts plaintext events after a PII annotation was added.
|
|
6
6
|
export { createPiiBackfillStateTable, piiAnnotationFingerprint, piiBackfillStateTable, runPiiEventBackfill, } from "./pii-event-backfill.js";
|
|
7
|
-
// tableName → projection-name,
|
|
7
|
+
// tableName → projection-name, for application-side projection rebuilds.
|
|
8
8
|
export { buildProjectionTableIndex } from "./projection-table-index.js";
|