@objectstack/metadata 17.0.0-rc.0 → 17.0.0-rc.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.
- package/CHANGELOG.md +3594 -0
- package/dist/index.cjs +353 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -17
- package/dist/index.d.ts +146 -17
- package/dist/index.js +348 -94
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +353 -102
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +348 -94
- package/dist/node.js.map +1 -1
- package/package.json +12 -10
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as System from '@objectstack/spec/system';
|
|
2
2
|
import { MetadataFormat, MetadataLoaderContract, MetadataLoadOptions, MetadataLoadResult, MetadataStats, MetadataSaveOptions, MetadataSaveResult, MetadataWatchEvent, MetadataManagerConfig, PackagePublishResult, MetadataHistoryQueryOptions, MetadataHistoryQueryResult, MetadataDiffResult, MetadataHistoryRecord, MetadataHistoryRetentionPolicy } from '@objectstack/spec/system';
|
|
3
|
-
export { MetadataCollectionInfo, MetadataDiffResult,
|
|
3
|
+
export { MetadataCollectionInfo, MetadataDiffResult, MetadataFormat, MetadataHistoryQueryOptions, MetadataHistoryQueryResult, MetadataHistoryRecord, MetadataHistoryRetentionPolicy, MetadataLoadOptions, MetadataLoadResult, MetadataLoaderContract, MetadataManagerConfig, MetadataSaveOptions, MetadataSaveResult, MetadataStats, MetadataWatchEvent } from '@objectstack/spec/system';
|
|
4
4
|
import { IMetadataService, IDataDriver, IDataEngine, IRealtimeService, MetadataWriteOptions, MetadataWatchCallback, MetadataWatchHandle, MetadataExportOptions, MetadataImportOptions, MetadataImportResult, MetadataTypeInfo, IPubSub, ISchemaDriver } from '@objectstack/spec/contracts';
|
|
5
|
-
export { IMetadataService, MetadataImportResult, MetadataTypeInfo, MetadataWatchCallback, MetadataWatchHandle } from '@objectstack/spec/contracts';
|
|
5
|
+
export { IMetadataService, MetadataExportOptions, MetadataImportOptions, MetadataImportResult, MetadataTypeInfo, MetadataWatchCallback, MetadataWatchHandle } from '@objectstack/spec/contracts';
|
|
6
6
|
import { MetadataTypeRegistryEntry, MetadataQuery, MetadataQueryResult, MetadataBulkResult, MetadataOverlay, MetadataValidationResult, MetadataDependency, MetadataPluginConfig } from '@objectstack/spec/kernel';
|
|
7
7
|
export { MetadataBulkResult, MetadataDependency, MetadataPluginConfig, MetadataPluginManifest, MetadataQuery, MetadataQueryResult, MetadataType, MetadataTypeRegistryEntry, MetadataValidationResult } from '@objectstack/spec/kernel';
|
|
8
8
|
import { Logger, Plugin, PluginContext } from '@objectstack/core';
|
|
@@ -199,6 +199,28 @@ declare class MetadataManager implements IMetadataService {
|
|
|
199
199
|
* @param service - An IRealtimeService instance for event publishing
|
|
200
200
|
*/
|
|
201
201
|
setRealtimeService(service: IRealtimeService): void;
|
|
202
|
+
/**
|
|
203
|
+
* Publish a realtime {@link RealtimeMetadataEvent} for a metadata write
|
|
204
|
+
* (#4602 — contract-first).
|
|
205
|
+
*
|
|
206
|
+
* What reaches a `subscribeMetadata` callback must BE the spec's
|
|
207
|
+
* `MetadataEvent` (`@objectstack/spec/api`): `id` (uuid) at the top level,
|
|
208
|
+
* flattened `metadataType`/`name`/`definition`, `userId` when the write
|
|
209
|
+
* carried an actor. The transport keeps its `RealtimeEventPayload`
|
|
210
|
+
* envelope — `payload` carries the complete `MetadataEvent`, and the client
|
|
211
|
+
* SDK unwraps + validates it at the boundary.
|
|
212
|
+
*
|
|
213
|
+
* Two loud-by-design gates:
|
|
214
|
+
* - `MetadataEventType` is a CLOSED enum. A metadata type outside it has
|
|
215
|
+
* no declared realtime event contract, so we skip publishing (debug log)
|
|
216
|
+
* instead of emitting an event every compliant consumer must reject.
|
|
217
|
+
* Declared = enforced; widening coverage means widening the spec enum,
|
|
218
|
+
* not producing off-contract events.
|
|
219
|
+
* - The event body is `MetadataEventSchema.parse`d before publish, so a
|
|
220
|
+
* malformed producer fails here (warn log, event not published) rather
|
|
221
|
+
* than delivering a lie downstream.
|
|
222
|
+
*/
|
|
223
|
+
private publishRealtimeMetadataEvent;
|
|
202
224
|
/**
|
|
203
225
|
* Register a new metadata loader (data source)
|
|
204
226
|
*/
|
|
@@ -444,8 +466,32 @@ declare class MetadataManager implements IMetadataService {
|
|
|
444
466
|
/**
|
|
445
467
|
* Load a single metadata item from loaders.
|
|
446
468
|
* Iterates through registered loaders until found.
|
|
469
|
+
*
|
|
470
|
+
* Returns `null` both when no loader HAS the item and when every loader
|
|
471
|
+
* FAILED — see {@link loadDiagnosed} when the caller must tell those apart.
|
|
447
472
|
*/
|
|
448
473
|
load<T = any>(type: string, name: string, options?: MetadataLoadOptions): Promise<T | null>;
|
|
474
|
+
/**
|
|
475
|
+
* `load`, plus whether the answer can be trusted as complete.
|
|
476
|
+
*
|
|
477
|
+
* [ADR-0110 D3] A miss and an outage are different facts with opposite
|
|
478
|
+
* security meanings, and plain `load` cannot express the difference: a
|
|
479
|
+
* loader that throws is warn-logged and skipped, so a database the metadata
|
|
480
|
+
* plane cannot reach returns the same `null` as a name that was never
|
|
481
|
+
* declared. Callers that gate on a declaration MUST NOT read that `null` as
|
|
482
|
+
* "the author declared no gate" — an availability failure would silently
|
|
483
|
+
* widen access (the REST `/actions` route's fail-open branch, #3935).
|
|
484
|
+
*
|
|
485
|
+
* `degraded` is true when at least one loader threw AND no loader answered
|
|
486
|
+
* with the item. The posture is deliberately conservative: with a loader
|
|
487
|
+
* down we cannot prove the item is absent, so we decline to claim it is.
|
|
488
|
+
* A clean miss (every loader answered, none had it) is NOT degraded.
|
|
489
|
+
*/
|
|
490
|
+
loadDiagnosed<T = any>(type: string, name: string, options?: MetadataLoadOptions): Promise<{
|
|
491
|
+
data: T | null;
|
|
492
|
+
degraded: boolean;
|
|
493
|
+
errors: string[];
|
|
494
|
+
}>;
|
|
449
495
|
/**
|
|
450
496
|
* Load multiple metadata items from loaders.
|
|
451
497
|
* Aggregates results from all loaders.
|
|
@@ -568,23 +614,38 @@ interface MetadataPluginOptions {
|
|
|
568
614
|
config?: Partial<MetadataPluginConfig>;
|
|
569
615
|
/** Organization ID for metadata-scoped consumers; MetadataPlugin itself does not persist runtime metadata. */
|
|
570
616
|
organizationId?: string;
|
|
571
|
-
/**
|
|
617
|
+
/**
|
|
618
|
+
* Environment ID used by local artifact envelopes and metadata-scoped
|
|
619
|
+
* consumers. (The v5.0 rename retired the "project ID" wording this
|
|
620
|
+
* comment used to carry; see ADR-0006.)
|
|
621
|
+
*/
|
|
572
622
|
environmentId?: string;
|
|
573
623
|
/**
|
|
574
|
-
* When set, MetadataPlugin loads metadata from
|
|
575
|
-
* the filesystem
|
|
576
|
-
*
|
|
624
|
+
* When set, MetadataPlugin loads metadata from a compiled artifact instead
|
|
625
|
+
* of scanning the filesystem, honored by all three bootstrap modes
|
|
626
|
+
* (`eager` / `lazy` / `artifact-only`) — see `start()`.
|
|
627
|
+
*
|
|
628
|
+
* `path` is a filesystem path, or an `http(s)://` URL fetched verbatim —
|
|
629
|
+
* the control plane's public artifact route
|
|
630
|
+
* (`/pub/v1/environments/:id/artifact[?commit=…]`) serves exactly such
|
|
631
|
+
* URLs, so a sealed runtime can boot straight off a published revision.
|
|
632
|
+
* Remote reads honor `fetchTimeoutMs` / `OS_ARTIFACT_FETCH_TIMEOUT_MS`;
|
|
633
|
+
* the artifact-file HMR watcher ({@link artifactWatch}) applies to
|
|
634
|
+
* non-URL paths only.
|
|
635
|
+
*
|
|
636
|
+
* `local-file` is the only mode. A second `artifact-api` mode (a
|
|
637
|
+
* Bearer-authenticated control-plane pull) existed here through v17 with
|
|
638
|
+
* zero consumers in any repo — the cloud runtime uses its own
|
|
639
|
+
* `ArtifactApiClient`, and package distribution into a running OSS
|
|
640
|
+
* instance goes through `@objectstack/cloud-connection` — and was removed
|
|
641
|
+
* when #4246 forced the declared-vs-enforced question. `start()` rejects
|
|
642
|
+
* an unknown mode loudly rather than silently scanning the filesystem
|
|
643
|
+
* instead.
|
|
577
644
|
*/
|
|
578
645
|
artifactSource?: {
|
|
579
646
|
mode: 'local-file';
|
|
580
647
|
path: string;
|
|
581
648
|
fetchTimeoutMs?: number;
|
|
582
|
-
} | {
|
|
583
|
-
mode: 'artifact-api';
|
|
584
|
-
url: string;
|
|
585
|
-
token?: string;
|
|
586
|
-
commitId?: string;
|
|
587
|
-
fetchTimeoutMs?: number;
|
|
588
649
|
};
|
|
589
650
|
/**
|
|
590
651
|
* Register the `sys_metadata` + `sys_metadata_history` storage objects
|
|
@@ -615,6 +676,20 @@ declare class MetadataPlugin implements Plugin {
|
|
|
615
676
|
name: string;
|
|
616
677
|
type: string;
|
|
617
678
|
version: string;
|
|
679
|
+
/**
|
|
680
|
+
* Services init() UNCONDITIONALLY registers (ADR-0116, #4131) — lets the
|
|
681
|
+
* kernel name this plugin when a consumer requires `metadata` before it
|
|
682
|
+
* initializes.
|
|
683
|
+
*/
|
|
684
|
+
providesServices: string[];
|
|
685
|
+
/**
|
|
686
|
+
* init() registers the metadata system objects through the `manifest`
|
|
687
|
+
* service ObjectQLPlugin provides — order-if-present so that
|
|
688
|
+
* registration is deterministic instead of "whichever init ran first"
|
|
689
|
+
* (ADR-0116, #4471). Soft, not hard: without an engine the plugin
|
|
690
|
+
* degrades on purpose (objects are discovered via the legacy fallback).
|
|
691
|
+
*/
|
|
692
|
+
optionalDependencies: string[];
|
|
618
693
|
private manager;
|
|
619
694
|
private options;
|
|
620
695
|
private repository?;
|
|
@@ -663,11 +738,19 @@ declare class MetadataPlugin implements Plugin {
|
|
|
663
738
|
* logged but never blocks the reload.
|
|
664
739
|
*/
|
|
665
740
|
private _reloadAndAnnounce;
|
|
666
|
-
private _loadFromLocalFile;
|
|
667
741
|
/**
|
|
668
|
-
*
|
|
742
|
+
* @param opts.optional When true, a LOCAL artifact file that does not exist
|
|
743
|
+
* is "nothing compiled yet" rather than a fault: log and return, leaving
|
|
744
|
+
* the manager empty and the artifact watcher armed so the first
|
|
745
|
+
* `os compile` hydrates the running server (#4085). Callers pass it for
|
|
746
|
+
* the `eager` / `lazy` bootstrap modes — the development-platform paths,
|
|
747
|
+
* where an app is optional. `artifact-only` (sealed runtime) does NOT:
|
|
748
|
+
* there the artifact IS the deployment, so its absence must fail loudly
|
|
749
|
+
* instead of silently serving an empty runtime. Only ENOENT is tolerated;
|
|
750
|
+
* a present-but-unreadable artifact (malformed JSON, bad permissions) and
|
|
751
|
+
* every remote-URL failure stay fatal.
|
|
669
752
|
*/
|
|
670
|
-
private
|
|
753
|
+
private _loadFromLocalFile;
|
|
671
754
|
private _loadFromFileSystem;
|
|
672
755
|
}
|
|
673
756
|
|
|
@@ -844,6 +927,19 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
844
927
|
private trackHistory;
|
|
845
928
|
private schemaReady;
|
|
846
929
|
private historySchemaReady;
|
|
930
|
+
/**
|
|
931
|
+
* Whether the loud "DDL failed" report has already been printed for the
|
|
932
|
+
* metadata table / history table respectively. AGENTS.md → "Degradation log
|
|
933
|
+
* levels": say it **once**, at the first degradation, not once per retry.
|
|
934
|
+
*/
|
|
935
|
+
private schemaFailureReported;
|
|
936
|
+
private historySchemaFailureReported;
|
|
937
|
+
/**
|
|
938
|
+
* Same once-only discipline for the #4825 seam: the history table is readable
|
|
939
|
+
* or it is not, and repeating the report per skipped write turns a real
|
|
940
|
+
* degradation into noise people learn to skim.
|
|
941
|
+
*/
|
|
942
|
+
private historySeqFailureReported;
|
|
847
943
|
/** (type, name) → metadata payload — primes `load()` */
|
|
848
944
|
private readonly loadCache?;
|
|
849
945
|
/** type → array of payloads — primes `loadMany()` */
|
|
@@ -881,6 +977,24 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
881
977
|
* Reads `MAX(event_seq) + 1` for the configured `organization_id`.
|
|
882
978
|
* Legacy path — not transactional, so concurrent writes can collide.
|
|
883
979
|
* The canonical (transactional) producer is `SysMetadataRepository`.
|
|
980
|
+
*
|
|
981
|
+
* #4825 (same shape as #4728, rule from #4632) — discriminate by error TYPE.
|
|
982
|
+
* This used to `catch { return 1 }`, with a comment that named BOTH reasons a
|
|
983
|
+
* read can fail and then answered both the same way. Exactly one of them is
|
|
984
|
+
* benign: the history table has not been provisioned, so there is no row to
|
|
985
|
+
* be inconsistent with and 1 genuinely IS the next number. Every other reason
|
|
986
|
+
* — connection drop, timeout, insufficient privileges — means the rows are
|
|
987
|
+
* still there and simply were not seen, and answering 1 against a table with
|
|
988
|
+
* N rows **collides with existing rows**: the insert succeeds, the log stays
|
|
989
|
+
* empty, and `event_seq` (the ordering key that history listing and rollback
|
|
990
|
+
* targeting both stand on) is silently wrong from then on. Note this is the
|
|
991
|
+
* costlier half of the #4728 family — not bytes that never landed, but bytes
|
|
992
|
+
* that landed *wrong*, which no retry and no restart repairs.
|
|
993
|
+
*
|
|
994
|
+
* @throws The underlying driver error, unchanged, for every non-benign read
|
|
995
|
+
* failure. Deliberate: a sequence number this method cannot derive
|
|
996
|
+
* from data it actually read is not a number it may invent. The
|
|
997
|
+
* caller ({@link createHistoryRecord}) owns the consequence.
|
|
884
998
|
*/
|
|
885
999
|
private nextEventSeq;
|
|
886
1000
|
/**
|
|
@@ -915,8 +1029,23 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
915
1029
|
*/
|
|
916
1030
|
private createHistoryRecord;
|
|
917
1031
|
/**
|
|
918
|
-
*
|
|
919
|
-
*
|
|
1032
|
+
* Once-per-process dedupe for stored-row conversion notices — `load` /
|
|
1033
|
+
* `loadMany` are hot read paths (cached, but re-hit on every TTL expiry),
|
|
1034
|
+
* so a legacy row must warn once, not once per cache miss.
|
|
1035
|
+
*/
|
|
1036
|
+
private storedConversionWarned;
|
|
1037
|
+
/**
|
|
1038
|
+
* Convert a LIVE database row to a metadata payload.
|
|
1039
|
+
*
|
|
1040
|
+
* Parses the JSON `metadata` column back into an object, then replays the
|
|
1041
|
+
* full ADR-0087 conversion chain over it (#3903): rows written under a past
|
|
1042
|
+
* protocol are served canonical, exactly like the metadata-protocol's
|
|
1043
|
+
* `sys_metadata` seams. History rows do NOT pass through here — history
|
|
1044
|
+
* readers parse inline and stay verbatim, as a record of what was written.
|
|
1045
|
+
*
|
|
1046
|
+
* `flow` is skipped for the same reason the protocol skips it: flow-node
|
|
1047
|
+
* conversions need the automation engine's live executor registry for their
|
|
1048
|
+
* open-namespace conflict guard; flows canonicalize at `registerFlow`.
|
|
920
1049
|
*/
|
|
921
1050
|
private rowToData;
|
|
922
1051
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as System from '@objectstack/spec/system';
|
|
2
2
|
import { MetadataFormat, MetadataLoaderContract, MetadataLoadOptions, MetadataLoadResult, MetadataStats, MetadataSaveOptions, MetadataSaveResult, MetadataWatchEvent, MetadataManagerConfig, PackagePublishResult, MetadataHistoryQueryOptions, MetadataHistoryQueryResult, MetadataDiffResult, MetadataHistoryRecord, MetadataHistoryRetentionPolicy } from '@objectstack/spec/system';
|
|
3
|
-
export { MetadataCollectionInfo, MetadataDiffResult,
|
|
3
|
+
export { MetadataCollectionInfo, MetadataDiffResult, MetadataFormat, MetadataHistoryQueryOptions, MetadataHistoryQueryResult, MetadataHistoryRecord, MetadataHistoryRetentionPolicy, MetadataLoadOptions, MetadataLoadResult, MetadataLoaderContract, MetadataManagerConfig, MetadataSaveOptions, MetadataSaveResult, MetadataStats, MetadataWatchEvent } from '@objectstack/spec/system';
|
|
4
4
|
import { IMetadataService, IDataDriver, IDataEngine, IRealtimeService, MetadataWriteOptions, MetadataWatchCallback, MetadataWatchHandle, MetadataExportOptions, MetadataImportOptions, MetadataImportResult, MetadataTypeInfo, IPubSub, ISchemaDriver } from '@objectstack/spec/contracts';
|
|
5
|
-
export { IMetadataService, MetadataImportResult, MetadataTypeInfo, MetadataWatchCallback, MetadataWatchHandle } from '@objectstack/spec/contracts';
|
|
5
|
+
export { IMetadataService, MetadataExportOptions, MetadataImportOptions, MetadataImportResult, MetadataTypeInfo, MetadataWatchCallback, MetadataWatchHandle } from '@objectstack/spec/contracts';
|
|
6
6
|
import { MetadataTypeRegistryEntry, MetadataQuery, MetadataQueryResult, MetadataBulkResult, MetadataOverlay, MetadataValidationResult, MetadataDependency, MetadataPluginConfig } from '@objectstack/spec/kernel';
|
|
7
7
|
export { MetadataBulkResult, MetadataDependency, MetadataPluginConfig, MetadataPluginManifest, MetadataQuery, MetadataQueryResult, MetadataType, MetadataTypeRegistryEntry, MetadataValidationResult } from '@objectstack/spec/kernel';
|
|
8
8
|
import { Logger, Plugin, PluginContext } from '@objectstack/core';
|
|
@@ -199,6 +199,28 @@ declare class MetadataManager implements IMetadataService {
|
|
|
199
199
|
* @param service - An IRealtimeService instance for event publishing
|
|
200
200
|
*/
|
|
201
201
|
setRealtimeService(service: IRealtimeService): void;
|
|
202
|
+
/**
|
|
203
|
+
* Publish a realtime {@link RealtimeMetadataEvent} for a metadata write
|
|
204
|
+
* (#4602 — contract-first).
|
|
205
|
+
*
|
|
206
|
+
* What reaches a `subscribeMetadata` callback must BE the spec's
|
|
207
|
+
* `MetadataEvent` (`@objectstack/spec/api`): `id` (uuid) at the top level,
|
|
208
|
+
* flattened `metadataType`/`name`/`definition`, `userId` when the write
|
|
209
|
+
* carried an actor. The transport keeps its `RealtimeEventPayload`
|
|
210
|
+
* envelope — `payload` carries the complete `MetadataEvent`, and the client
|
|
211
|
+
* SDK unwraps + validates it at the boundary.
|
|
212
|
+
*
|
|
213
|
+
* Two loud-by-design gates:
|
|
214
|
+
* - `MetadataEventType` is a CLOSED enum. A metadata type outside it has
|
|
215
|
+
* no declared realtime event contract, so we skip publishing (debug log)
|
|
216
|
+
* instead of emitting an event every compliant consumer must reject.
|
|
217
|
+
* Declared = enforced; widening coverage means widening the spec enum,
|
|
218
|
+
* not producing off-contract events.
|
|
219
|
+
* - The event body is `MetadataEventSchema.parse`d before publish, so a
|
|
220
|
+
* malformed producer fails here (warn log, event not published) rather
|
|
221
|
+
* than delivering a lie downstream.
|
|
222
|
+
*/
|
|
223
|
+
private publishRealtimeMetadataEvent;
|
|
202
224
|
/**
|
|
203
225
|
* Register a new metadata loader (data source)
|
|
204
226
|
*/
|
|
@@ -444,8 +466,32 @@ declare class MetadataManager implements IMetadataService {
|
|
|
444
466
|
/**
|
|
445
467
|
* Load a single metadata item from loaders.
|
|
446
468
|
* Iterates through registered loaders until found.
|
|
469
|
+
*
|
|
470
|
+
* Returns `null` both when no loader HAS the item and when every loader
|
|
471
|
+
* FAILED — see {@link loadDiagnosed} when the caller must tell those apart.
|
|
447
472
|
*/
|
|
448
473
|
load<T = any>(type: string, name: string, options?: MetadataLoadOptions): Promise<T | null>;
|
|
474
|
+
/**
|
|
475
|
+
* `load`, plus whether the answer can be trusted as complete.
|
|
476
|
+
*
|
|
477
|
+
* [ADR-0110 D3] A miss and an outage are different facts with opposite
|
|
478
|
+
* security meanings, and plain `load` cannot express the difference: a
|
|
479
|
+
* loader that throws is warn-logged and skipped, so a database the metadata
|
|
480
|
+
* plane cannot reach returns the same `null` as a name that was never
|
|
481
|
+
* declared. Callers that gate on a declaration MUST NOT read that `null` as
|
|
482
|
+
* "the author declared no gate" — an availability failure would silently
|
|
483
|
+
* widen access (the REST `/actions` route's fail-open branch, #3935).
|
|
484
|
+
*
|
|
485
|
+
* `degraded` is true when at least one loader threw AND no loader answered
|
|
486
|
+
* with the item. The posture is deliberately conservative: with a loader
|
|
487
|
+
* down we cannot prove the item is absent, so we decline to claim it is.
|
|
488
|
+
* A clean miss (every loader answered, none had it) is NOT degraded.
|
|
489
|
+
*/
|
|
490
|
+
loadDiagnosed<T = any>(type: string, name: string, options?: MetadataLoadOptions): Promise<{
|
|
491
|
+
data: T | null;
|
|
492
|
+
degraded: boolean;
|
|
493
|
+
errors: string[];
|
|
494
|
+
}>;
|
|
449
495
|
/**
|
|
450
496
|
* Load multiple metadata items from loaders.
|
|
451
497
|
* Aggregates results from all loaders.
|
|
@@ -568,23 +614,38 @@ interface MetadataPluginOptions {
|
|
|
568
614
|
config?: Partial<MetadataPluginConfig>;
|
|
569
615
|
/** Organization ID for metadata-scoped consumers; MetadataPlugin itself does not persist runtime metadata. */
|
|
570
616
|
organizationId?: string;
|
|
571
|
-
/**
|
|
617
|
+
/**
|
|
618
|
+
* Environment ID used by local artifact envelopes and metadata-scoped
|
|
619
|
+
* consumers. (The v5.0 rename retired the "project ID" wording this
|
|
620
|
+
* comment used to carry; see ADR-0006.)
|
|
621
|
+
*/
|
|
572
622
|
environmentId?: string;
|
|
573
623
|
/**
|
|
574
|
-
* When set, MetadataPlugin loads metadata from
|
|
575
|
-
* the filesystem
|
|
576
|
-
*
|
|
624
|
+
* When set, MetadataPlugin loads metadata from a compiled artifact instead
|
|
625
|
+
* of scanning the filesystem, honored by all three bootstrap modes
|
|
626
|
+
* (`eager` / `lazy` / `artifact-only`) — see `start()`.
|
|
627
|
+
*
|
|
628
|
+
* `path` is a filesystem path, or an `http(s)://` URL fetched verbatim —
|
|
629
|
+
* the control plane's public artifact route
|
|
630
|
+
* (`/pub/v1/environments/:id/artifact[?commit=…]`) serves exactly such
|
|
631
|
+
* URLs, so a sealed runtime can boot straight off a published revision.
|
|
632
|
+
* Remote reads honor `fetchTimeoutMs` / `OS_ARTIFACT_FETCH_TIMEOUT_MS`;
|
|
633
|
+
* the artifact-file HMR watcher ({@link artifactWatch}) applies to
|
|
634
|
+
* non-URL paths only.
|
|
635
|
+
*
|
|
636
|
+
* `local-file` is the only mode. A second `artifact-api` mode (a
|
|
637
|
+
* Bearer-authenticated control-plane pull) existed here through v17 with
|
|
638
|
+
* zero consumers in any repo — the cloud runtime uses its own
|
|
639
|
+
* `ArtifactApiClient`, and package distribution into a running OSS
|
|
640
|
+
* instance goes through `@objectstack/cloud-connection` — and was removed
|
|
641
|
+
* when #4246 forced the declared-vs-enforced question. `start()` rejects
|
|
642
|
+
* an unknown mode loudly rather than silently scanning the filesystem
|
|
643
|
+
* instead.
|
|
577
644
|
*/
|
|
578
645
|
artifactSource?: {
|
|
579
646
|
mode: 'local-file';
|
|
580
647
|
path: string;
|
|
581
648
|
fetchTimeoutMs?: number;
|
|
582
|
-
} | {
|
|
583
|
-
mode: 'artifact-api';
|
|
584
|
-
url: string;
|
|
585
|
-
token?: string;
|
|
586
|
-
commitId?: string;
|
|
587
|
-
fetchTimeoutMs?: number;
|
|
588
649
|
};
|
|
589
650
|
/**
|
|
590
651
|
* Register the `sys_metadata` + `sys_metadata_history` storage objects
|
|
@@ -615,6 +676,20 @@ declare class MetadataPlugin implements Plugin {
|
|
|
615
676
|
name: string;
|
|
616
677
|
type: string;
|
|
617
678
|
version: string;
|
|
679
|
+
/**
|
|
680
|
+
* Services init() UNCONDITIONALLY registers (ADR-0116, #4131) — lets the
|
|
681
|
+
* kernel name this plugin when a consumer requires `metadata` before it
|
|
682
|
+
* initializes.
|
|
683
|
+
*/
|
|
684
|
+
providesServices: string[];
|
|
685
|
+
/**
|
|
686
|
+
* init() registers the metadata system objects through the `manifest`
|
|
687
|
+
* service ObjectQLPlugin provides — order-if-present so that
|
|
688
|
+
* registration is deterministic instead of "whichever init ran first"
|
|
689
|
+
* (ADR-0116, #4471). Soft, not hard: without an engine the plugin
|
|
690
|
+
* degrades on purpose (objects are discovered via the legacy fallback).
|
|
691
|
+
*/
|
|
692
|
+
optionalDependencies: string[];
|
|
618
693
|
private manager;
|
|
619
694
|
private options;
|
|
620
695
|
private repository?;
|
|
@@ -663,11 +738,19 @@ declare class MetadataPlugin implements Plugin {
|
|
|
663
738
|
* logged but never blocks the reload.
|
|
664
739
|
*/
|
|
665
740
|
private _reloadAndAnnounce;
|
|
666
|
-
private _loadFromLocalFile;
|
|
667
741
|
/**
|
|
668
|
-
*
|
|
742
|
+
* @param opts.optional When true, a LOCAL artifact file that does not exist
|
|
743
|
+
* is "nothing compiled yet" rather than a fault: log and return, leaving
|
|
744
|
+
* the manager empty and the artifact watcher armed so the first
|
|
745
|
+
* `os compile` hydrates the running server (#4085). Callers pass it for
|
|
746
|
+
* the `eager` / `lazy` bootstrap modes — the development-platform paths,
|
|
747
|
+
* where an app is optional. `artifact-only` (sealed runtime) does NOT:
|
|
748
|
+
* there the artifact IS the deployment, so its absence must fail loudly
|
|
749
|
+
* instead of silently serving an empty runtime. Only ENOENT is tolerated;
|
|
750
|
+
* a present-but-unreadable artifact (malformed JSON, bad permissions) and
|
|
751
|
+
* every remote-URL failure stay fatal.
|
|
669
752
|
*/
|
|
670
|
-
private
|
|
753
|
+
private _loadFromLocalFile;
|
|
671
754
|
private _loadFromFileSystem;
|
|
672
755
|
}
|
|
673
756
|
|
|
@@ -844,6 +927,19 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
844
927
|
private trackHistory;
|
|
845
928
|
private schemaReady;
|
|
846
929
|
private historySchemaReady;
|
|
930
|
+
/**
|
|
931
|
+
* Whether the loud "DDL failed" report has already been printed for the
|
|
932
|
+
* metadata table / history table respectively. AGENTS.md → "Degradation log
|
|
933
|
+
* levels": say it **once**, at the first degradation, not once per retry.
|
|
934
|
+
*/
|
|
935
|
+
private schemaFailureReported;
|
|
936
|
+
private historySchemaFailureReported;
|
|
937
|
+
/**
|
|
938
|
+
* Same once-only discipline for the #4825 seam: the history table is readable
|
|
939
|
+
* or it is not, and repeating the report per skipped write turns a real
|
|
940
|
+
* degradation into noise people learn to skim.
|
|
941
|
+
*/
|
|
942
|
+
private historySeqFailureReported;
|
|
847
943
|
/** (type, name) → metadata payload — primes `load()` */
|
|
848
944
|
private readonly loadCache?;
|
|
849
945
|
/** type → array of payloads — primes `loadMany()` */
|
|
@@ -881,6 +977,24 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
881
977
|
* Reads `MAX(event_seq) + 1` for the configured `organization_id`.
|
|
882
978
|
* Legacy path — not transactional, so concurrent writes can collide.
|
|
883
979
|
* The canonical (transactional) producer is `SysMetadataRepository`.
|
|
980
|
+
*
|
|
981
|
+
* #4825 (same shape as #4728, rule from #4632) — discriminate by error TYPE.
|
|
982
|
+
* This used to `catch { return 1 }`, with a comment that named BOTH reasons a
|
|
983
|
+
* read can fail and then answered both the same way. Exactly one of them is
|
|
984
|
+
* benign: the history table has not been provisioned, so there is no row to
|
|
985
|
+
* be inconsistent with and 1 genuinely IS the next number. Every other reason
|
|
986
|
+
* — connection drop, timeout, insufficient privileges — means the rows are
|
|
987
|
+
* still there and simply were not seen, and answering 1 against a table with
|
|
988
|
+
* N rows **collides with existing rows**: the insert succeeds, the log stays
|
|
989
|
+
* empty, and `event_seq` (the ordering key that history listing and rollback
|
|
990
|
+
* targeting both stand on) is silently wrong from then on. Note this is the
|
|
991
|
+
* costlier half of the #4728 family — not bytes that never landed, but bytes
|
|
992
|
+
* that landed *wrong*, which no retry and no restart repairs.
|
|
993
|
+
*
|
|
994
|
+
* @throws The underlying driver error, unchanged, for every non-benign read
|
|
995
|
+
* failure. Deliberate: a sequence number this method cannot derive
|
|
996
|
+
* from data it actually read is not a number it may invent. The
|
|
997
|
+
* caller ({@link createHistoryRecord}) owns the consequence.
|
|
884
998
|
*/
|
|
885
999
|
private nextEventSeq;
|
|
886
1000
|
/**
|
|
@@ -915,8 +1029,23 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
915
1029
|
*/
|
|
916
1030
|
private createHistoryRecord;
|
|
917
1031
|
/**
|
|
918
|
-
*
|
|
919
|
-
*
|
|
1032
|
+
* Once-per-process dedupe for stored-row conversion notices — `load` /
|
|
1033
|
+
* `loadMany` are hot read paths (cached, but re-hit on every TTL expiry),
|
|
1034
|
+
* so a legacy row must warn once, not once per cache miss.
|
|
1035
|
+
*/
|
|
1036
|
+
private storedConversionWarned;
|
|
1037
|
+
/**
|
|
1038
|
+
* Convert a LIVE database row to a metadata payload.
|
|
1039
|
+
*
|
|
1040
|
+
* Parses the JSON `metadata` column back into an object, then replays the
|
|
1041
|
+
* full ADR-0087 conversion chain over it (#3903): rows written under a past
|
|
1042
|
+
* protocol are served canonical, exactly like the metadata-protocol's
|
|
1043
|
+
* `sys_metadata` seams. History rows do NOT pass through here — history
|
|
1044
|
+
* readers parse inline and stay verbatim, as a record of what was written.
|
|
1045
|
+
*
|
|
1046
|
+
* `flow` is skipped for the same reason the protocol skips it: flow-node
|
|
1047
|
+
* conversions need the automation engine's live executor registry for their
|
|
1048
|
+
* open-namespace conflict guard; flows canonicalize at `registerFlow`.
|
|
920
1049
|
*/
|
|
921
1050
|
private rowToData;
|
|
922
1051
|
/**
|