@objectstack/metadata 3.0.11 → 3.1.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/metadata@3.0.11 build /home/runner/work/spec/spec/packages/metadata
2
+ > @objectstack/metadata@3.1.0 build /home/runner/work/spec/spec/packages/metadata
3
3
  > tsup --config ../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- ESM dist/index.mjs 56.54 KB
14
- ESM dist/index.mjs.map 115.11 KB
15
- ESM ⚡️ Build success in 186ms
16
- CJS dist/index.js 58.68 KB
17
- CJS dist/index.js.map 117.29 KB
18
- CJS ⚡️ Build success in 203ms
13
+ CJS dist/index.js 63.63 KB
14
+ CJS dist/index.js.map 126.48 KB
15
+ CJS ⚡️ Build success in 194ms
16
+ ESM dist/index.mjs 61.49 KB
17
+ ESM dist/index.mjs.map 124.30 KB
18
+ ESM ⚡️ Build success in 199ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 19997ms
21
- DTS dist/index.d.mts 30.41 KB
22
- DTS dist/index.d.ts 30.41 KB
20
+ DTS ⚡️ Build success in 22968ms
21
+ DTS dist/index.d.mts 31.26 KB
22
+ DTS dist/index.d.ts 31.26 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @objectstack/metadata
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [0088830]
8
+ - @objectstack/spec@3.1.0
9
+ - @objectstack/core@3.1.0
10
+ - @objectstack/types@3.1.0
11
+
3
12
  ## 3.0.11
4
13
 
5
14
  ### Patch Changes
package/README.md CHANGED
@@ -86,6 +86,7 @@ The `MetadataManager` is the main orchestrator. It provides:
86
86
  - **Core CRUD**: `register`, `get`, `list`, `unregister`, `exists`, `listNames`
87
87
  - **Convenience**: `getObject`, `listObjects`
88
88
  - **Package Management**: `unregisterPackage` — unload all metadata from a package
89
+ - **Package Publishing**: `publishPackage`, `revertPackage`, `getPublished` — atomic package-level metadata publishing
89
90
  - **Query / Search**: `query` with filtering, pagination, sorting by type/scope/state/tags
90
91
  - **Bulk Operations**: `bulkRegister`, `bulkUnregister` with error handling
91
92
  - **Import / Export**: `exportMetadata`, `importMetadata` with conflict resolution (skip/overwrite/merge)
@@ -201,6 +202,34 @@ const plugin = MetadataPlugin({
201
202
  kernel.use(plugin);
202
203
  ```
203
204
 
205
+ ## Package Publishing
206
+
207
+ ObjectStack supports **package-level metadata publishing** — all metadata items within a package are published atomically.
208
+
209
+ ### Publish a Package
210
+
211
+ ```typescript
212
+ const result = await manager.publishPackage('com.acme.crm', {
213
+ publishedBy: 'admin',
214
+ validate: true,
215
+ });
216
+ // result: { success: true, version: 2, itemsPublished: 5, publishedAt: '...' }
217
+ ```
218
+
219
+ ### Revert to Last Published State
220
+
221
+ ```typescript
222
+ await manager.revertPackage('com.acme.crm');
223
+ // All items restored to their publishedDefinition snapshots
224
+ ```
225
+
226
+ ### Get Published Version (Runtime Serving)
227
+
228
+ ```typescript
229
+ const published = await manager.getPublished('object', 'opportunity');
230
+ // Returns publishedDefinition if exists, else current definition
231
+ ```
232
+
204
233
  ## Package Structure
205
234
 
206
235
  ```
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { MetadataFormat, MetadataLoaderContract, MetadataLoadOptions, MetadataLoadResult, MetadataStats, MetadataSaveOptions, MetadataSaveResult, MetadataWatchEvent, MetadataManagerConfig } from '@objectstack/spec/system';
1
+ import { MetadataFormat, MetadataLoaderContract, MetadataLoadOptions, MetadataLoadResult, MetadataStats, MetadataSaveOptions, MetadataSaveResult, MetadataWatchEvent, MetadataManagerConfig, PackagePublishResult } from '@objectstack/spec/system';
2
2
  export { MetadataCollectionInfo, MetadataExportOptions, MetadataFormat, MetadataImportOptions, MetadataLoadOptions, MetadataLoadResult, MetadataLoaderContract, MetadataManagerConfig, MetadataSaveOptions, MetadataSaveResult, MetadataStats, MetadataWatchEvent } from '@objectstack/spec/system';
3
3
  import { IMetadataService, IDataDriver, MetadataWatchCallback, MetadataWatchHandle, MetadataExportOptions, MetadataImportOptions, MetadataImportResult, MetadataTypeInfo, ISchemaDriver } from '@objectstack/spec/contracts';
4
4
  export { IMetadataService, MetadataImportResult, MetadataTypeInfo, MetadataWatchCallback, MetadataWatchHandle } from '@objectstack/spec/contracts';
@@ -225,6 +225,28 @@ declare class MetadataManager implements IMetadataService {
225
225
  * Unregister all metadata items from a specific package
226
226
  */
227
227
  unregisterPackage(packageName: string): Promise<void>;
228
+ /**
229
+ * Publish an entire package:
230
+ * 1. Validate all draft items
231
+ * 2. Snapshot all items in the package (publishedDefinition = clone(metadata))
232
+ * 3. Increment version
233
+ * 4. Set all items state → active
234
+ */
235
+ publishPackage(packageId: string, options?: {
236
+ changeNote?: string;
237
+ publishedBy?: string;
238
+ validate?: boolean;
239
+ }): Promise<PackagePublishResult>;
240
+ /**
241
+ * Revert entire package to last published state.
242
+ * Restores all metadata definitions from their published snapshots.
243
+ */
244
+ revertPackage(packageId: string): Promise<void>;
245
+ /**
246
+ * Get the published version of any metadata item (for runtime serving).
247
+ * Returns publishedDefinition if exists, else current definition.
248
+ */
249
+ getPublished(type: string, name: string): Promise<unknown | undefined>;
228
250
  /**
229
251
  * Query metadata items with filtering, sorting, and pagination
230
252
  */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MetadataFormat, MetadataLoaderContract, MetadataLoadOptions, MetadataLoadResult, MetadataStats, MetadataSaveOptions, MetadataSaveResult, MetadataWatchEvent, MetadataManagerConfig } from '@objectstack/spec/system';
1
+ import { MetadataFormat, MetadataLoaderContract, MetadataLoadOptions, MetadataLoadResult, MetadataStats, MetadataSaveOptions, MetadataSaveResult, MetadataWatchEvent, MetadataManagerConfig, PackagePublishResult } from '@objectstack/spec/system';
2
2
  export { MetadataCollectionInfo, MetadataExportOptions, MetadataFormat, MetadataImportOptions, MetadataLoadOptions, MetadataLoadResult, MetadataLoaderContract, MetadataManagerConfig, MetadataSaveOptions, MetadataSaveResult, MetadataStats, MetadataWatchEvent } from '@objectstack/spec/system';
3
3
  import { IMetadataService, IDataDriver, MetadataWatchCallback, MetadataWatchHandle, MetadataExportOptions, MetadataImportOptions, MetadataImportResult, MetadataTypeInfo, ISchemaDriver } from '@objectstack/spec/contracts';
4
4
  export { IMetadataService, MetadataImportResult, MetadataTypeInfo, MetadataWatchCallback, MetadataWatchHandle } from '@objectstack/spec/contracts';
@@ -225,6 +225,28 @@ declare class MetadataManager implements IMetadataService {
225
225
  * Unregister all metadata items from a specific package
226
226
  */
227
227
  unregisterPackage(packageName: string): Promise<void>;
228
+ /**
229
+ * Publish an entire package:
230
+ * 1. Validate all draft items
231
+ * 2. Snapshot all items in the package (publishedDefinition = clone(metadata))
232
+ * 3. Increment version
233
+ * 4. Set all items state → active
234
+ */
235
+ publishPackage(packageId: string, options?: {
236
+ changeNote?: string;
237
+ publishedBy?: string;
238
+ validate?: boolean;
239
+ }): Promise<PackagePublishResult>;
240
+ /**
241
+ * Revert entire package to last published state.
242
+ * Restores all metadata definitions from their published snapshots.
243
+ */
244
+ revertPackage(packageId: string): Promise<void>;
245
+ /**
246
+ * Get the published version of any metadata item (for runtime serving).
247
+ * Returns publishedDefinition if exists, else current definition.
248
+ */
249
+ getPublished(type: string, name: string): Promise<unknown | undefined>;
228
250
  /**
229
251
  * Query metadata items with filtering, sorting, and pagination
230
252
  */
package/dist/index.js CHANGED
@@ -825,6 +825,156 @@ var MetadataManager = class {
825
825
  }
826
826
  }
827
827
  }
828
+ /**
829
+ * Publish an entire package:
830
+ * 1. Validate all draft items
831
+ * 2. Snapshot all items in the package (publishedDefinition = clone(metadata))
832
+ * 3. Increment version
833
+ * 4. Set all items state → active
834
+ */
835
+ async publishPackage(packageId, options) {
836
+ const now = (/* @__PURE__ */ new Date()).toISOString();
837
+ const shouldValidate = options?.validate !== false;
838
+ const publishedBy = options?.publishedBy;
839
+ const packageItems = [];
840
+ for (const [type, typeStore] of this.registry) {
841
+ for (const [name, data] of typeStore) {
842
+ const meta = data;
843
+ if (meta?.packageId === packageId || meta?.package === packageId) {
844
+ packageItems.push({ type, name, data: meta });
845
+ }
846
+ }
847
+ }
848
+ if (packageItems.length === 0) {
849
+ return {
850
+ success: false,
851
+ packageId,
852
+ version: 0,
853
+ publishedAt: now,
854
+ itemsPublished: 0,
855
+ validationErrors: [{ type: "", name: "", message: `No metadata items found for package '${packageId}'` }]
856
+ };
857
+ }
858
+ if (shouldValidate) {
859
+ const validationErrors = [];
860
+ for (const item of packageItems) {
861
+ const result = await this.validate(item.type, item.data);
862
+ if (!result.valid && result.errors) {
863
+ for (const err of result.errors) {
864
+ validationErrors.push({
865
+ type: item.type,
866
+ name: item.name,
867
+ message: err.message
868
+ });
869
+ }
870
+ }
871
+ }
872
+ const packageItemKeys = new Set(packageItems.map((i) => `${i.type}:${i.name}`));
873
+ for (const item of packageItems) {
874
+ const deps = await this.getDependencies(item.type, item.name);
875
+ for (const dep of deps) {
876
+ const depKey = `${dep.targetType}:${dep.targetName}`;
877
+ if (packageItemKeys.has(depKey)) continue;
878
+ const depItem = await this.get(dep.targetType, dep.targetName);
879
+ if (!depItem) {
880
+ validationErrors.push({
881
+ type: item.type,
882
+ name: item.name,
883
+ message: `Dependency '${dep.targetType}:${dep.targetName}' not found`
884
+ });
885
+ } else {
886
+ const depMeta = depItem;
887
+ if (depMeta.publishedDefinition === void 0 && depMeta.state !== "active") {
888
+ validationErrors.push({
889
+ type: item.type,
890
+ name: item.name,
891
+ message: `Dependency '${dep.targetType}:${dep.targetName}' is not published`
892
+ });
893
+ }
894
+ }
895
+ }
896
+ }
897
+ if (validationErrors.length > 0) {
898
+ return {
899
+ success: false,
900
+ packageId,
901
+ version: 0,
902
+ publishedAt: now,
903
+ itemsPublished: 0,
904
+ validationErrors
905
+ };
906
+ }
907
+ }
908
+ let maxVersion = 0;
909
+ for (const item of packageItems) {
910
+ const v = typeof item.data.version === "number" ? item.data.version : 0;
911
+ if (v > maxVersion) maxVersion = v;
912
+ }
913
+ const newVersion = maxVersion + 1;
914
+ for (const item of packageItems) {
915
+ const updated = {
916
+ ...item.data,
917
+ publishedDefinition: structuredClone(item.data.metadata ?? item.data),
918
+ publishedAt: now,
919
+ publishedBy: publishedBy ?? item.data.publishedBy,
920
+ version: newVersion,
921
+ state: "active"
922
+ };
923
+ await this.register(item.type, item.name, updated);
924
+ }
925
+ return {
926
+ success: true,
927
+ packageId,
928
+ version: newVersion,
929
+ publishedAt: now,
930
+ itemsPublished: packageItems.length
931
+ };
932
+ }
933
+ /**
934
+ * Revert entire package to last published state.
935
+ * Restores all metadata definitions from their published snapshots.
936
+ */
937
+ async revertPackage(packageId) {
938
+ const packageItems = [];
939
+ for (const [type, typeStore] of this.registry) {
940
+ for (const [name, data] of typeStore) {
941
+ const meta = data;
942
+ if (meta?.packageId === packageId || meta?.package === packageId) {
943
+ packageItems.push({ type, name, data: meta });
944
+ }
945
+ }
946
+ }
947
+ if (packageItems.length === 0) {
948
+ throw new Error(`No metadata items found for package '${packageId}'`);
949
+ }
950
+ const hasPublished = packageItems.some((item) => item.data.publishedDefinition !== void 0);
951
+ if (!hasPublished) {
952
+ throw new Error(`Package '${packageId}' has never been published`);
953
+ }
954
+ for (const item of packageItems) {
955
+ if (item.data.publishedDefinition !== void 0) {
956
+ const reverted = {
957
+ ...item.data,
958
+ metadata: structuredClone(item.data.publishedDefinition),
959
+ state: "active"
960
+ };
961
+ await this.register(item.type, item.name, reverted);
962
+ }
963
+ }
964
+ }
965
+ /**
966
+ * Get the published version of any metadata item (for runtime serving).
967
+ * Returns publishedDefinition if exists, else current definition.
968
+ */
969
+ async getPublished(type, name) {
970
+ const item = await this.get(type, name);
971
+ if (!item) return void 0;
972
+ const meta = item;
973
+ if (meta.publishedDefinition !== void 0) {
974
+ return meta.publishedDefinition;
975
+ }
976
+ return meta.metadata ?? item;
977
+ }
828
978
  // ==========================================
829
979
  // Query / Search
830
980
  // ==========================================