@arcote.tech/arc-cli 0.8.5 → 0.8.7

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/dist/index.js CHANGED
@@ -10191,8 +10191,72 @@ function apply(state, patches, applyOptions) {
10191
10191
  }
10192
10192
  return create(state, mutate, Object.assign(Object.assign({}, applyOptions), { enablePatches: false }));
10193
10193
  }
10194
- function hasLocalStorage() {
10195
- return typeof localStorage !== "undefined";
10194
+ function hasDocument() {
10195
+ return typeof document !== "undefined";
10196
+ }
10197
+ function signalCrossTabChange() {
10198
+ if (typeof localStorage === "undefined")
10199
+ return;
10200
+ try {
10201
+ localStorage.setItem(TOKEN_BUMP_KEY, String(Date.now()));
10202
+ } catch {}
10203
+ }
10204
+ function isSecureContext() {
10205
+ return typeof location !== "undefined" && location.protocol === "https:";
10206
+ }
10207
+ function tokenCookieName(scopeKey) {
10208
+ return scopeKey === "default" ? COOKIE_PREFIX : `${COOKIE_PREFIX}_${scopeKey.replace(/:/g, ".")}`;
10209
+ }
10210
+ function scopeKeyFromCookieName(name) {
10211
+ if (name === COOKIE_PREFIX)
10212
+ return "default";
10213
+ if (!name.startsWith(`${COOKIE_PREFIX}_`))
10214
+ return null;
10215
+ return name.slice(COOKIE_PREFIX.length + 1).replace(/\./g, ":");
10216
+ }
10217
+ function maxAgeFromExp(exp) {
10218
+ const THIRTY_DAYS = 30 * 24 * 60 * 60;
10219
+ if (typeof exp !== "number" || !Number.isFinite(exp))
10220
+ return THIRTY_DAYS;
10221
+ const expMs = exp > 1000000000000 ? exp : exp * 1000;
10222
+ const secs = Math.floor((expMs - Date.now()) / 1000);
10223
+ return secs > 0 ? secs : THIRTY_DAYS;
10224
+ }
10225
+ function writeTokenCookie(scopeKey, jwt, exp) {
10226
+ if (!hasDocument())
10227
+ return;
10228
+ const attrs = [
10229
+ "Path=/",
10230
+ "SameSite=Lax",
10231
+ `Max-Age=${maxAgeFromExp(exp)}`
10232
+ ];
10233
+ if (isSecureContext())
10234
+ attrs.push("Secure");
10235
+ document.cookie = `${tokenCookieName(scopeKey)}=${encodeURIComponent(jwt)}; ${attrs.join("; ")}`;
10236
+ signalCrossTabChange();
10237
+ }
10238
+ function removeTokenCookie(scopeKey) {
10239
+ if (!hasDocument())
10240
+ return;
10241
+ document.cookie = `${tokenCookieName(scopeKey)}=; Path=/; SameSite=Lax; Max-Age=0`;
10242
+ signalCrossTabChange();
10243
+ }
10244
+ function readAllTokenCookies() {
10245
+ if (!hasDocument() || !document.cookie)
10246
+ return [];
10247
+ const out = [];
10248
+ for (const part of document.cookie.split(";")) {
10249
+ const eq = part.indexOf("=");
10250
+ if (eq < 0)
10251
+ continue;
10252
+ const scopeKey = scopeKeyFromCookieName(part.slice(0, eq).trim());
10253
+ if (scopeKey === null)
10254
+ continue;
10255
+ const jwt = decodeURIComponent(part.slice(eq + 1).trim());
10256
+ if (jwt)
10257
+ out.push({ scopeKey, jwt });
10258
+ }
10259
+ return out;
10196
10260
  }
10197
10261
  function notifyTokenChange(scope) {
10198
10262
  if (typeof window !== "undefined") {
@@ -10208,24 +10272,21 @@ class AuthAdapter {
10208
10272
  constructor(options) {
10209
10273
  this.namespace = options?.storageNamespace || undefined;
10210
10274
  }
10211
- storageKey(scope) {
10212
- return this.namespace ? `${TOKEN_PREFIX}${this.namespace}:${scope}` : TOKEN_PREFIX + scope;
10275
+ scopeKey(scope) {
10276
+ return this.namespace ? `${this.namespace}:${scope}` : scope;
10213
10277
  }
10214
10278
  setToken(token, scope = "default") {
10215
10279
  if (!token) {
10216
10280
  this.scopes.delete(scope);
10217
- if (hasLocalStorage()) {
10218
- localStorage.removeItem(this.storageKey(scope));
10219
- notifyTokenChange(scope);
10220
- }
10281
+ removeTokenCookie(this.scopeKey(scope));
10282
+ notifyTokenChange(scope);
10221
10283
  return;
10222
10284
  }
10223
10285
  try {
10224
10286
  const parts = token.split(".");
10225
10287
  if (parts.length !== 3) {
10226
10288
  this.scopes.delete(scope);
10227
- if (hasLocalStorage())
10228
- localStorage.removeItem(this.storageKey(scope));
10289
+ removeTokenCookie(this.scopeKey(scope));
10229
10290
  return;
10230
10291
  }
10231
10292
  const payload = JSON.parse(atob(parts[1]));
@@ -10238,49 +10299,45 @@ class AuthAdapter {
10238
10299
  exp: payload.exp
10239
10300
  }
10240
10301
  });
10241
- if (hasLocalStorage()) {
10242
- localStorage.setItem(this.storageKey(scope), token);
10243
- notifyTokenChange(scope);
10244
- }
10302
+ writeTokenCookie(this.scopeKey(scope), token, payload.exp);
10303
+ notifyTokenChange(scope);
10245
10304
  } catch {
10246
10305
  this.scopes.delete(scope);
10247
- if (hasLocalStorage())
10248
- localStorage.removeItem(this.storageKey(scope));
10306
+ removeTokenCookie(this.scopeKey(scope));
10249
10307
  }
10250
10308
  }
10251
10309
  setDecoded(decoded, scope = "default") {
10252
10310
  this.scopes.set(scope, { raw: "", decoded });
10253
10311
  }
10254
10312
  loadPersisted() {
10255
- if (!hasLocalStorage())
10256
- return;
10257
- const prefix = this.namespace ? `${TOKEN_PREFIX}${this.namespace}:` : TOKEN_PREFIX;
10258
- for (let i = 0;i < localStorage.length; i++) {
10259
- const key = localStorage.key(i);
10260
- if (key?.startsWith(prefix)) {
10261
- const scope = key.slice(prefix.length);
10262
- if (!this.namespace && scope.includes(":"))
10313
+ const nsPrefix = this.namespace ? `${this.namespace}:` : "";
10314
+ for (const { scopeKey, jwt } of readAllTokenCookies()) {
10315
+ let scope;
10316
+ if (this.namespace) {
10317
+ if (!scopeKey.startsWith(nsPrefix))
10263
10318
  continue;
10264
- const raw = localStorage.getItem(key);
10265
- if (raw) {
10266
- try {
10267
- const parts = raw.split(".");
10268
- if (parts.length !== 3)
10269
- continue;
10270
- const payload = JSON.parse(atob(parts[1]));
10271
- this.scopes.set(scope, {
10272
- raw,
10273
- decoded: {
10274
- tokenName: payload.tokenName,
10275
- params: payload.params || {},
10276
- iat: payload.iat,
10277
- exp: payload.exp
10278
- }
10279
- });
10280
- } catch {
10281
- localStorage.removeItem(key);
10319
+ scope = scopeKey.slice(nsPrefix.length);
10320
+ } else {
10321
+ if (scopeKey.includes(":"))
10322
+ continue;
10323
+ scope = scopeKey;
10324
+ }
10325
+ try {
10326
+ const parts = jwt.split(".");
10327
+ if (parts.length !== 3)
10328
+ continue;
10329
+ const payload = JSON.parse(atob(parts[1]));
10330
+ this.scopes.set(scope, {
10331
+ raw: jwt,
10332
+ decoded: {
10333
+ tokenName: payload.tokenName,
10334
+ params: payload.params || {},
10335
+ iat: payload.iat,
10336
+ exp: payload.exp
10282
10337
  }
10283
- }
10338
+ });
10339
+ } catch {
10340
+ removeTokenCookie(scopeKey);
10284
10341
  }
10285
10342
  }
10286
10343
  }
@@ -10312,10 +10369,8 @@ class AuthAdapter {
10312
10369
  return Array.from(this.scopes.keys());
10313
10370
  }
10314
10371
  clear() {
10315
- if (hasLocalStorage()) {
10316
- for (const scope of this.scopes.keys()) {
10317
- localStorage.removeItem(this.storageKey(scope));
10318
- }
10372
+ for (const scope of this.scopes.keys()) {
10373
+ removeTokenCookie(this.scopeKey(scope));
10319
10374
  }
10320
10375
  this.scopes.clear();
10321
10376
  }
@@ -14595,7 +14650,7 @@ var Operation, PROXY_DRAFT, RAW_RETURN_SYMBOL, iteratorSymbol, dataTypes, intern
14595
14650
  }
14596
14651
  return returnValue(result);
14597
14652
  };
14598
- }, create, constructorString, TOKEN_PREFIX = "arc:token:", DEFAULT_TIMEOUT_MS = 8000, provider = null, latestSync = null, syncSeq = 0, eventWireInstanceCounter = 0, EVENT_TABLES, arrayValidator, ArcArray, objectValidator, ArcObject, ScopedDataStorage, ArcAuthenticationError, ArcAuthorizationError2, ArcValidationError2, ArcPrimitive, stringValidator, ArcString, ArcContextElement, ArcBoolean, ArcId, numberValidator, ArcNumber, ArcEvent, ArcCommand, ArcListener, ArcRoute, ForkedStoreState, ForkedDataStorage, MasterStoreState, MasterDataStorage2, dateValidator, _te, _td, originCache, originStackCache, originError, CLOSE, Query, PostgresError, Errors, types2, Identifier, Parameter, Builder, defaultHandlers, builders, serializers, parsers, mergeUserTypes = function(types22) {
14653
+ }, create, constructorString, COOKIE_PREFIX = "arc_token", TOKEN_BUMP_KEY = "arc:token-bump", DEFAULT_TIMEOUT_MS = 8000, provider = null, latestSync = null, syncSeq = 0, eventWireInstanceCounter = 0, EVENT_TABLES, arrayValidator, ArcArray, objectValidator, ArcObject, ScopedDataStorage, ArcAuthenticationError, ArcAuthorizationError2, ArcValidationError2, ArcPrimitive, stringValidator, ArcString, ArcContextElement, ArcBoolean, ArcId, numberValidator, ArcNumber, ArcEvent, ArcCommand, ArcListener, ArcRoute, ForkedStoreState, ForkedDataStorage, MasterStoreState, MasterDataStorage2, dateValidator, _te, _td, originCache, originStackCache, originError, CLOSE, Query, PostgresError, Errors, types2, Identifier, Parameter, Builder, defaultHandlers, builders, serializers, parsers, mergeUserTypes = function(types22) {
14599
14654
  const user = typeHandlers(types22 || {});
14600
14655
  return {
14601
14656
  serializers: Object.assign({}, serializers, user.serializers),
@@ -17838,8 +17893,72 @@ function apply2(state, patches, applyOptions) {
17838
17893
  }
17839
17894
  return create2(state, mutate, Object.assign(Object.assign({}, applyOptions), { enablePatches: false }));
17840
17895
  }
17841
- function hasLocalStorage2() {
17842
- return typeof localStorage !== "undefined";
17896
+ function hasDocument2() {
17897
+ return typeof document !== "undefined";
17898
+ }
17899
+ function signalCrossTabChange2() {
17900
+ if (typeof localStorage === "undefined")
17901
+ return;
17902
+ try {
17903
+ localStorage.setItem(TOKEN_BUMP_KEY2, String(Date.now()));
17904
+ } catch {}
17905
+ }
17906
+ function isSecureContext2() {
17907
+ return typeof location !== "undefined" && location.protocol === "https:";
17908
+ }
17909
+ function tokenCookieName2(scopeKey) {
17910
+ return scopeKey === "default" ? COOKIE_PREFIX2 : `${COOKIE_PREFIX2}_${scopeKey.replace(/:/g, ".")}`;
17911
+ }
17912
+ function scopeKeyFromCookieName2(name) {
17913
+ if (name === COOKIE_PREFIX2)
17914
+ return "default";
17915
+ if (!name.startsWith(`${COOKIE_PREFIX2}_`))
17916
+ return null;
17917
+ return name.slice(COOKIE_PREFIX2.length + 1).replace(/\./g, ":");
17918
+ }
17919
+ function maxAgeFromExp2(exp) {
17920
+ const THIRTY_DAYS = 30 * 24 * 60 * 60;
17921
+ if (typeof exp !== "number" || !Number.isFinite(exp))
17922
+ return THIRTY_DAYS;
17923
+ const expMs = exp > 1000000000000 ? exp : exp * 1000;
17924
+ const secs = Math.floor((expMs - Date.now()) / 1000);
17925
+ return secs > 0 ? secs : THIRTY_DAYS;
17926
+ }
17927
+ function writeTokenCookie2(scopeKey, jwt, exp) {
17928
+ if (!hasDocument2())
17929
+ return;
17930
+ const attrs = [
17931
+ "Path=/",
17932
+ "SameSite=Lax",
17933
+ `Max-Age=${maxAgeFromExp2(exp)}`
17934
+ ];
17935
+ if (isSecureContext2())
17936
+ attrs.push("Secure");
17937
+ document.cookie = `${tokenCookieName2(scopeKey)}=${encodeURIComponent(jwt)}; ${attrs.join("; ")}`;
17938
+ signalCrossTabChange2();
17939
+ }
17940
+ function removeTokenCookie2(scopeKey) {
17941
+ if (!hasDocument2())
17942
+ return;
17943
+ document.cookie = `${tokenCookieName2(scopeKey)}=; Path=/; SameSite=Lax; Max-Age=0`;
17944
+ signalCrossTabChange2();
17945
+ }
17946
+ function readAllTokenCookies2() {
17947
+ if (!hasDocument2() || !document.cookie)
17948
+ return [];
17949
+ const out = [];
17950
+ for (const part of document.cookie.split(";")) {
17951
+ const eq = part.indexOf("=");
17952
+ if (eq < 0)
17953
+ continue;
17954
+ const scopeKey = scopeKeyFromCookieName2(part.slice(0, eq).trim());
17955
+ if (scopeKey === null)
17956
+ continue;
17957
+ const jwt = decodeURIComponent(part.slice(eq + 1).trim());
17958
+ if (jwt)
17959
+ out.push({ scopeKey, jwt });
17960
+ }
17961
+ return out;
17843
17962
  }
17844
17963
  function notifyTokenChange2(scope) {
17845
17964
  if (typeof window !== "undefined") {
@@ -17855,24 +17974,21 @@ class AuthAdapter2 {
17855
17974
  constructor(options) {
17856
17975
  this.namespace = options?.storageNamespace || undefined;
17857
17976
  }
17858
- storageKey(scope) {
17859
- return this.namespace ? `${TOKEN_PREFIX2}${this.namespace}:${scope}` : TOKEN_PREFIX2 + scope;
17977
+ scopeKey(scope) {
17978
+ return this.namespace ? `${this.namespace}:${scope}` : scope;
17860
17979
  }
17861
17980
  setToken(token, scope = "default") {
17862
17981
  if (!token) {
17863
17982
  this.scopes.delete(scope);
17864
- if (hasLocalStorage2()) {
17865
- localStorage.removeItem(this.storageKey(scope));
17866
- notifyTokenChange2(scope);
17867
- }
17983
+ removeTokenCookie2(this.scopeKey(scope));
17984
+ notifyTokenChange2(scope);
17868
17985
  return;
17869
17986
  }
17870
17987
  try {
17871
17988
  const parts = token.split(".");
17872
17989
  if (parts.length !== 3) {
17873
17990
  this.scopes.delete(scope);
17874
- if (hasLocalStorage2())
17875
- localStorage.removeItem(this.storageKey(scope));
17991
+ removeTokenCookie2(this.scopeKey(scope));
17876
17992
  return;
17877
17993
  }
17878
17994
  const payload = JSON.parse(atob(parts[1]));
@@ -17885,49 +18001,45 @@ class AuthAdapter2 {
17885
18001
  exp: payload.exp
17886
18002
  }
17887
18003
  });
17888
- if (hasLocalStorage2()) {
17889
- localStorage.setItem(this.storageKey(scope), token);
17890
- notifyTokenChange2(scope);
17891
- }
18004
+ writeTokenCookie2(this.scopeKey(scope), token, payload.exp);
18005
+ notifyTokenChange2(scope);
17892
18006
  } catch {
17893
18007
  this.scopes.delete(scope);
17894
- if (hasLocalStorage2())
17895
- localStorage.removeItem(this.storageKey(scope));
18008
+ removeTokenCookie2(this.scopeKey(scope));
17896
18009
  }
17897
18010
  }
17898
18011
  setDecoded(decoded, scope = "default") {
17899
18012
  this.scopes.set(scope, { raw: "", decoded });
17900
18013
  }
17901
18014
  loadPersisted() {
17902
- if (!hasLocalStorage2())
17903
- return;
17904
- const prefix = this.namespace ? `${TOKEN_PREFIX2}${this.namespace}:` : TOKEN_PREFIX2;
17905
- for (let i = 0;i < localStorage.length; i++) {
17906
- const key = localStorage.key(i);
17907
- if (key?.startsWith(prefix)) {
17908
- const scope = key.slice(prefix.length);
17909
- if (!this.namespace && scope.includes(":"))
18015
+ const nsPrefix = this.namespace ? `${this.namespace}:` : "";
18016
+ for (const { scopeKey, jwt } of readAllTokenCookies2()) {
18017
+ let scope;
18018
+ if (this.namespace) {
18019
+ if (!scopeKey.startsWith(nsPrefix))
17910
18020
  continue;
17911
- const raw = localStorage.getItem(key);
17912
- if (raw) {
17913
- try {
17914
- const parts = raw.split(".");
17915
- if (parts.length !== 3)
17916
- continue;
17917
- const payload = JSON.parse(atob(parts[1]));
17918
- this.scopes.set(scope, {
17919
- raw,
17920
- decoded: {
17921
- tokenName: payload.tokenName,
17922
- params: payload.params || {},
17923
- iat: payload.iat,
17924
- exp: payload.exp
17925
- }
17926
- });
17927
- } catch {
17928
- localStorage.removeItem(key);
18021
+ scope = scopeKey.slice(nsPrefix.length);
18022
+ } else {
18023
+ if (scopeKey.includes(":"))
18024
+ continue;
18025
+ scope = scopeKey;
18026
+ }
18027
+ try {
18028
+ const parts = jwt.split(".");
18029
+ if (parts.length !== 3)
18030
+ continue;
18031
+ const payload = JSON.parse(atob(parts[1]));
18032
+ this.scopes.set(scope, {
18033
+ raw: jwt,
18034
+ decoded: {
18035
+ tokenName: payload.tokenName,
18036
+ params: payload.params || {},
18037
+ iat: payload.iat,
18038
+ exp: payload.exp
17929
18039
  }
17930
- }
18040
+ });
18041
+ } catch {
18042
+ removeTokenCookie2(scopeKey);
17931
18043
  }
17932
18044
  }
17933
18045
  }
@@ -17959,10 +18071,8 @@ class AuthAdapter2 {
17959
18071
  return Array.from(this.scopes.keys());
17960
18072
  }
17961
18073
  clear() {
17962
- if (hasLocalStorage2()) {
17963
- for (const scope of this.scopes.keys()) {
17964
- localStorage.removeItem(this.storageKey(scope));
17965
- }
18074
+ for (const scope of this.scopes.keys()) {
18075
+ removeTokenCookie2(this.scopeKey(scope));
17966
18076
  }
17967
18077
  this.scopes.clear();
17968
18078
  }
@@ -20673,7 +20783,7 @@ var Operation2, PROXY_DRAFT2, RAW_RETURN_SYMBOL2, iteratorSymbol2, dataTypes2, i
20673
20783
  }
20674
20784
  return returnValue(result);
20675
20785
  };
20676
- }, create2, constructorString2, TOKEN_PREFIX2 = "arc:token:", DEFAULT_TIMEOUT_MS2 = 8000, provider2 = null, latestSync2 = null, syncSeq2 = 0, eventWireInstanceCounter2 = 0, EVENT_TABLES2, arrayValidator2, ArcArray2, objectValidator2, ArcObject2, ScopedDataStorage2, ArcAuthenticationError2, ArcAuthorizationError3, ArcValidationError3, ArcPrimitive2, stringValidator2, ArcString2, ArcContextElement2, ArcBoolean2, ArcId2, numberValidator2, ArcNumber2, ArcEvent2, ArcCommand2, ArcListener2, ArcRoute2, ForkedStoreState2, ForkedDataStorage2, MasterStoreState2, MasterDataStorage3, dateValidator2, _te2, _td2, SQLiteReadWriteTransaction, createSQLiteAdapterFactory = (db) => {
20786
+ }, create2, constructorString2, COOKIE_PREFIX2 = "arc_token", TOKEN_BUMP_KEY2 = "arc:token-bump", DEFAULT_TIMEOUT_MS2 = 8000, provider2 = null, latestSync2 = null, syncSeq2 = 0, eventWireInstanceCounter2 = 0, EVENT_TABLES2, arrayValidator2, ArcArray2, objectValidator2, ArcObject2, ScopedDataStorage2, ArcAuthenticationError2, ArcAuthorizationError3, ArcValidationError3, ArcPrimitive2, stringValidator2, ArcString2, ArcContextElement2, ArcBoolean2, ArcId2, numberValidator2, ArcNumber2, ArcEvent2, ArcCommand2, ArcListener2, ArcRoute2, ForkedStoreState2, ForkedDataStorage2, MasterStoreState2, MasterDataStorage3, dateValidator2, _te2, _td2, SQLiteReadWriteTransaction, createSQLiteAdapterFactory = (db) => {
20677
20787
  return async (context) => {
20678
20788
  const adapter = new SQLiteAdapter(db, context);
20679
20789
  await adapter.initialize();
@@ -31692,7 +31802,7 @@ function workspaceSourcePlugin(srcByName) {
31692
31802
  }
31693
31803
  };
31694
31804
  }
31695
- var SERVER_ONLY_NODE_BUILTINS = new Set([
31805
+ var SERVER_ONLY_NODE_BUILTINS = [
31696
31806
  "crypto",
31697
31807
  "stream",
31698
31808
  "zlib",
@@ -31720,15 +31830,13 @@ var SERVER_ONLY_NODE_BUILTINS = new Set([
31720
31830
  "diagnostics_channel",
31721
31831
  "module",
31722
31832
  "constants"
31723
- ]);
31833
+ ];
31834
+ var NODE_STUB_FILTER = new RegExp(`^(node:)?(${SERVER_ONLY_NODE_BUILTINS.map((b) => b.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")).join("|")})$`);
31724
31835
  function nodeServerBuiltinStubPlugin() {
31725
31836
  return {
31726
31837
  name: "node-server-builtin-stub",
31727
31838
  setup(build2) {
31728
- build2.onResolve({ filter: /^(node:)?[a-z_/]+$/ }, (args) => {
31729
- const bare = args.path.replace(/^node:/, "");
31730
- if (!SERVER_ONLY_NODE_BUILTINS.has(bare))
31731
- return;
31839
+ build2.onResolve({ filter: NODE_STUB_FILTER }, (args) => {
31732
31840
  return { path: args.path, namespace: "node-server-stub" };
31733
31841
  });
31734
31842
  build2.onLoad({ filter: /.*/, namespace: "node-server-stub" }, (args) => ({
@@ -31782,19 +31890,23 @@ function discoverPackages(rootDir) {
31782
31890
  if (pkg.name?.startsWith("@arcote.tech/"))
31783
31891
  continue;
31784
31892
  const pkgDir = join9(baseDir, entry);
31785
- const candidates = [
31786
- join9(pkgDir, "src", "index.ts"),
31787
- join9(pkgDir, "src", "index.tsx"),
31788
- join9(pkgDir, "index.ts"),
31789
- join9(pkgDir, "index.tsx")
31893
+ const entryCandidates = (variant) => [
31894
+ join9(pkgDir, "src", `index${variant}.ts`),
31895
+ join9(pkgDir, "src", `index${variant}.tsx`),
31896
+ join9(pkgDir, `index${variant}.ts`),
31897
+ join9(pkgDir, `index${variant}.tsx`)
31790
31898
  ];
31791
- const entrypoint = candidates.find((c) => existsSync8(c)) ?? null;
31899
+ const entrypoint = entryCandidates("").find((c) => existsSync8(c)) ?? null;
31792
31900
  if (!entrypoint)
31793
31901
  continue;
31902
+ const serverEntrypoint = entryCandidates(".server").find((c) => existsSync8(c)) ?? entrypoint;
31903
+ const clientEntrypoint = entryCandidates(".client").find((c) => existsSync8(c)) ?? entrypoint;
31794
31904
  results.push({
31795
31905
  name: pkg.name,
31796
31906
  path: join9(baseDir, entry),
31797
31907
  entrypoint,
31908
+ serverEntrypoint,
31909
+ clientEntrypoint,
31798
31910
  packageJson: pkg
31799
31911
  });
31800
31912
  }
@@ -31836,13 +31948,15 @@ function depVersionsHash(rootDir, pkg) {
31836
31948
  async function buildContextClient(pkg, rootDir, client, cache, noCache) {
31837
31949
  const unitId = `context-pkg:${pkg.name}:${client.name}`;
31838
31950
  const outDir = join9(pkg.path, "dist", client.name);
31951
+ const entry = client.target === "browser" ? pkg.clientEntrypoint : pkg.entrypoint;
31839
31952
  const inputHash = sha256OfJson({
31840
31953
  src: pkgSourceHash(pkg),
31841
31954
  pkg: pkg.packageJson,
31842
31955
  deps: depVersionsHash(rootDir, pkg),
31843
31956
  client: client.name,
31844
31957
  target: client.target,
31845
- defines: client.defines
31958
+ defines: client.defines,
31959
+ entry: relative3(pkg.path, entry)
31846
31960
  });
31847
31961
  if (!noCache && isCacheHit(cache, unitId, inputHash, [join9(outDir, "main", "index.js")])) {
31848
31962
  console.log(` \u2713 cached: ${pkg.name} (${client.name})`);
@@ -31853,7 +31967,7 @@ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
31853
31967
  const allDeps = pkg.packageJson.dependencies ?? {};
31854
31968
  const externals = [...peerDeps, ...Object.keys(allDeps)];
31855
31969
  const result = await Bun.build({
31856
- entrypoints: [pkg.entrypoint],
31970
+ entrypoints: [entry],
31857
31971
  outdir: join9(outDir, "main"),
31858
31972
  target: client.target,
31859
31973
  format: "esm",
@@ -31873,7 +31987,7 @@ async function buildContextClient(pkg, rootDir, client, cache, noCache) {
31873
31987
  }
31874
31988
  const globalsContent = Object.entries(client.defines).map(([k, v]) => `declare const ${k}: ${v};`).join(`
31875
31989
  `);
31876
- const declResult = await buildTypeDeclarations([pkg.entrypoint], outDir, dirname6(pkg.entrypoint), globalsContent);
31990
+ const declResult = await buildTypeDeclarations([entry], outDir, dirname6(entry), globalsContent);
31877
31991
  const declarationErrors = !declResult.success && declResult.errors.length > 0 ? declResult.errors.map((e) => `[${pkg.name}/${client.name}] ${e}`) : [];
31878
31992
  const outputHash = sha256OfDir(outDir);
31879
31993
  updateCache(cache, unitId, inputHash, { outputHash });
@@ -31928,7 +32042,7 @@ async function buildContextPackages(rootDir, packages, cache, noCache) {
31928
32042
  async function buildServerApp(rootDir, serverDir, packages, cache, noCache) {
31929
32043
  const contexts = packages.filter((p) => isContextPackage(p.packageJson));
31930
32044
  mkdirSync7(serverDir, { recursive: true });
31931
- const srcByName = new Map(packages.map((p) => [p.name, p.entrypoint]));
32045
+ const srcByName = new Map(packages.map((p) => [p.name, p.serverEntrypoint]));
31932
32046
  const externalSet = new Set(FRAMEWORK_PEERS);
31933
32047
  for (const p of packages) {
31934
32048
  for (const name of Object.keys(p.packageJson.peerDependencies ?? {})) {
@@ -31942,7 +32056,11 @@ async function buildServerApp(rootDir, serverDir, packages, cache, noCache) {
31942
32056
  const external = [...externalSet];
31943
32057
  const unitId = "server-app";
31944
32058
  const inputHash = sha256OfJson({
31945
- members: packages.map((p) => ({ name: p.name, src: pkgSourceHash(p) })).sort((a, b) => a.name.localeCompare(b.name)),
32059
+ members: packages.map((p) => ({
32060
+ name: p.name,
32061
+ src: pkgSourceHash(p),
32062
+ serverEntry: relative3(p.path, p.serverEntrypoint)
32063
+ })).sort((a, b) => a.name.localeCompare(b.name)),
31946
32064
  contexts: contexts.map((p) => p.name).sort(),
31947
32065
  external: [...external].sort(),
31948
32066
  defines: SERVER_DEFINES,
@@ -32184,11 +32302,11 @@ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollec
32184
32302
  const unitId = opts?.unitId ?? "browser-app";
32185
32303
  const allMembers = [];
32186
32304
  for (const m of publicMembers) {
32187
- allMembers.push({ name: m.pkg.name, group: "public", srcHash: pkgSourceHash(m.pkg) });
32305
+ allMembers.push({ name: m.pkg.name, group: "public", srcHash: pkgSourceHash(m.pkg), clientEntry: relative3(m.pkg.path, m.pkg.clientEntrypoint) });
32188
32306
  }
32189
32307
  for (const g of protectedGroups) {
32190
32308
  for (const m of g.members) {
32191
- allMembers.push({ name: m.pkg.name, group: g.name, srcHash: pkgSourceHash(m.pkg) });
32309
+ allMembers.push({ name: m.pkg.name, group: g.name, srcHash: pkgSourceHash(m.pkg), clientEntry: relative3(m.pkg.path, m.pkg.clientEntrypoint) });
32192
32310
  }
32193
32311
  }
32194
32312
  const inputHash = sha256OfJson({
@@ -32202,7 +32320,7 @@ async function buildBrowserApp(rootDir, outDir, plan, cache, noCache, i18nCollec
32202
32320
  exposeRuntime,
32203
32321
  minify,
32204
32322
  externals: federated ? [...SHELL_EXTERNALS] : [],
32205
- builderRev: 3
32323
+ builderRev: 4
32206
32324
  });
32207
32325
  if (!noCache && !buildStatsEnabled() && isCacheHit(cache, unitId, inputHash)) {
32208
32326
  const cached = cache.units[unitId]?.outputHashes;
@@ -32255,6 +32373,12 @@ export { startApp } from "@arcote.tech/platform";
32255
32373
  for (const g of protectedGroups)
32256
32374
  for (const m of g.members)
32257
32375
  allMemberPkgs.set(m.pkg.name, m.pkg);
32376
+ const clientByName = new Map;
32377
+ for (const pkg of allMemberPkgs.values()) {
32378
+ if (pkg.clientEntrypoint !== pkg.entrypoint) {
32379
+ clientByName.set(pkg.name, pkg.clientEntrypoint);
32380
+ }
32381
+ }
32258
32382
  const patchedPkgJsons = [];
32259
32383
  for (const pkg of allMemberPkgs.values()) {
32260
32384
  const pkgJsonPath = join9(pkg.path, "package.json");
@@ -32281,6 +32405,7 @@ export { startApp } from "@arcote.tech/platform";
32281
32405
  target: "browser",
32282
32406
  external: federated ? [...SHELL_EXTERNALS] : [],
32283
32407
  plugins: [
32408
+ ...clientByName.size > 0 ? [workspaceSourcePlugin(clientByName)] : [],
32284
32409
  ...federated ? [] : [singleReactPlugin(rootDir)],
32285
32410
  jsxDevShimPlugin(),
32286
32411
  nodeServerBuiltinStubPlugin(),
@@ -32672,7 +32797,7 @@ function walkTsFiles(dir, out) {
32672
32797
  if (entry.name === "__tests__")
32673
32798
  continue;
32674
32799
  walkTsFiles(abs, out);
32675
- } else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
32800
+ } else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith(".d.ts") && !/\.server\.tsx?$/.test(entry.name)) {
32676
32801
  out.push(abs);
32677
32802
  }
32678
32803
  }
@@ -36917,11 +37042,11 @@ async function runSurvey(appName) {
36917
37042
  });
36918
37043
  if (BD(serverType))
36919
37044
  cancel();
36920
- const location = await ue({
37045
+ const location2 = await ue({
36921
37046
  message: "Hetzner datacenter location",
36922
37047
  initialValue: "nbg1"
36923
37048
  });
36924
- if (BD(location))
37049
+ if (BD(location2))
36925
37050
  cancel();
36926
37051
  const firstEnv = Object.keys(envs)[0] ?? "host";
36927
37052
  const appSlug = sanitizeImageName(appName ?? "arc-app").replace(/[^a-z0-9-]+/g, "-");
@@ -36929,7 +37054,7 @@ async function runSurvey(appName) {
36929
37054
  const terraform = {
36930
37055
  provider: "hcloud",
36931
37056
  serverType,
36932
- location,
37057
+ location: location2,
36933
37058
  image: "ubuntu-22.04",
36934
37059
  tokenEnv,
36935
37060
  serverName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-cli",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "CLI tool for Arc framework",
5
5
  "module": "index.ts",
6
6
  "main": "dist/index.js",
@@ -12,13 +12,13 @@
12
12
  "build": "bun build --target=bun ./src/index.ts --outdir=dist --external @arcote.tech/arc --external @arcote.tech/arc-ds --external @arcote.tech/arc-react --external @arcote.tech/platform --external @arcote.tech/arc-map --external '@opentelemetry/*' && chmod +x dist/index.js"
13
13
  },
14
14
  "dependencies": {
15
- "@arcote.tech/arc": "^0.8.5",
16
- "@arcote.tech/arc-ds": "^0.8.5",
17
- "@arcote.tech/arc-react": "^0.8.5",
18
- "@arcote.tech/arc-host": "^0.8.5",
19
- "@arcote.tech/arc-adapter-db-sqlite": "^0.8.5",
20
- "@arcote.tech/arc-adapter-db-postgres": "^0.8.5",
21
- "@arcote.tech/arc-otel": "^0.8.5",
15
+ "@arcote.tech/arc": "^0.8.7",
16
+ "@arcote.tech/arc-ds": "^0.8.7",
17
+ "@arcote.tech/arc-react": "^0.8.7",
18
+ "@arcote.tech/arc-host": "^0.8.7",
19
+ "@arcote.tech/arc-adapter-db-sqlite": "^0.8.7",
20
+ "@arcote.tech/arc-adapter-db-postgres": "^0.8.7",
21
+ "@arcote.tech/arc-otel": "^0.8.7",
22
22
  "@opentelemetry/api": "^1.9.0",
23
23
  "@opentelemetry/api-logs": "^0.57.0",
24
24
  "@opentelemetry/core": "^1.30.0",
@@ -31,8 +31,8 @@
31
31
  "@opentelemetry/sdk-trace-base": "^1.30.0",
32
32
  "@opentelemetry/sdk-trace-node": "^1.30.0",
33
33
  "@opentelemetry/semantic-conventions": "^1.27.0",
34
- "@arcote.tech/platform": "^0.8.5",
35
- "@arcote.tech/arc-map": "^0.8.5",
34
+ "@arcote.tech/platform": "^0.8.7",
35
+ "@arcote.tech/arc-map": "^0.8.7",
36
36
  "@clack/prompts": "^0.9.0",
37
37
  "commander": "^11.1.0",
38
38
  "chokidar": "^3.5.3",
@@ -248,12 +248,27 @@ function workspaceSourcePlugin(srcByName: Map<string, string>): import("bun").Bu
248
248
  // CELOWO NIE stubujemy „przeglądarkowo-bezpiecznych" builtinów (buffer, util,
249
249
  // events, path, url, string_decoder, querystring, assert, process) — te bywają
250
250
  // realnie używane po stronie klienta i mają lekkie, działające polyfille.
251
- const SERVER_ONLY_NODE_BUILTINS = new Set([
251
+ const SERVER_ONLY_NODE_BUILTINS = [
252
252
  "crypto", "stream", "zlib", "fs", "fs/promises", "net", "tls", "http",
253
253
  "https", "http2", "dns", "dgram", "child_process", "worker_threads",
254
254
  "cluster", "os", "v8", "vm", "readline", "repl", "tty", "perf_hooks",
255
255
  "inspector", "async_hooks", "diagnostics_channel", "module", "constants",
256
- ]);
256
+ ];
257
+
258
+ /**
259
+ * Filtr onResolve dopasowujący DOKŁADNIE serwerowe builtiny (`node:crypto`,
260
+ * `crypto`, `fs/promises`, …) i NIC innego. KRYTYCZNE, że jest precyzyjny:
261
+ * szeroki filtr `/^(node:)?[a-z_/]+$/` łapał też gołe pakiety npm (np. `unified`,
262
+ * `vfile`, `micromark`), a samo przepuszczenie ich przez onResolve — nawet ze
263
+ * zwrotem `undefined` — korumpuje tree-shaking Buna pod minify: pakiet znika z
264
+ * bundla, ale zostaje wiszące wywołanie (`ReferenceError: X is not defined` w
265
+ * produkcji — react-markdown/`unified()` na stronach z markdownem).
266
+ */
267
+ const NODE_STUB_FILTER = new RegExp(
268
+ `^(node:)?(${SERVER_ONLY_NODE_BUILTINS.map((b) =>
269
+ b.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&"),
270
+ ).join("|")})$`,
271
+ );
257
272
 
258
273
  /**
259
274
  * Zastępuje serwerowe builtiny node (SERVER_ONLY_NODE_BUILTINS) rzucającym
@@ -266,9 +281,7 @@ function nodeServerBuiltinStubPlugin(): import("bun").BunPlugin {
266
281
  return {
267
282
  name: "node-server-builtin-stub",
268
283
  setup(build) {
269
- build.onResolve({ filter: /^(node:)?[a-z_/]+$/ }, (args) => {
270
- const bare = args.path.replace(/^node:/, "");
271
- if (!SERVER_ONLY_NODE_BUILTINS.has(bare)) return undefined;
284
+ build.onResolve({ filter: NODE_STUB_FILTER }, (args) => {
272
285
  return { path: args.path, namespace: "node-server-stub" };
273
286
  });
274
287
  build.onLoad({ filter: /.*/, namespace: "node-server-stub" }, (args) => ({
@@ -332,7 +345,12 @@ export const SERVER_EXTERNALS_FILE = "_externals.json";
332
345
  export interface WorkspacePackage {
333
346
  name: string;
334
347
  path: string;
348
+ /** Base entry (src/index.ts). Fallback for both targets. */
335
349
  entrypoint: string;
350
+ /** src/index.server.ts if present, else `entrypoint`. Used by the SERVER bundle. */
351
+ serverEntrypoint: string;
352
+ /** src/index.client.ts if present, else `entrypoint`. Used by the CLIENT (browser) bundle. */
353
+ clientEntrypoint: string;
336
354
  packageJson: Record<string, any>;
337
355
  }
338
356
 
@@ -374,19 +392,30 @@ export function discoverPackages(rootDir: string): WorkspacePackage[] {
374
392
  if (pkg.name?.startsWith("@arcote.tech/")) continue;
375
393
 
376
394
  const pkgDir = join(baseDir, entry);
377
- const candidates = [
378
- join(pkgDir, "src", "index.ts"),
379
- join(pkgDir, "src", "index.tsx"),
380
- join(pkgDir, "index.ts"),
381
- join(pkgDir, "index.tsx"),
395
+ const entryCandidates = (variant: "" | ".server" | ".client") => [
396
+ join(pkgDir, "src", `index${variant}.ts`),
397
+ join(pkgDir, "src", `index${variant}.tsx`),
398
+ join(pkgDir, `index${variant}.ts`),
399
+ join(pkgDir, `index${variant}.tsx`),
382
400
  ];
383
- const entrypoint = candidates.find((c) => existsSync(c)) ?? null;
401
+ const entrypoint = entryCandidates("").find((c) => existsSync(c)) ?? null;
384
402
  if (!entrypoint) continue;
385
403
 
404
+ // Per-target entry override by file convention: index.server.ts /
405
+ // index.client.ts. Absent → fall back to the base entry (zero change for
406
+ // packages that don't opt in). Lets seeds live in index.server.ts so they
407
+ // never enter the client graph (see seed-guard.ts).
408
+ const serverEntrypoint =
409
+ entryCandidates(".server").find((c) => existsSync(c)) ?? entrypoint;
410
+ const clientEntrypoint =
411
+ entryCandidates(".client").find((c) => existsSync(c)) ?? entrypoint;
412
+
386
413
  results.push({
387
414
  name: pkg.name,
388
415
  path: join(baseDir, entry),
389
416
  entrypoint,
417
+ serverEntrypoint,
418
+ clientEntrypoint,
390
419
  packageJson: pkg,
391
420
  });
392
421
  }
@@ -467,6 +496,12 @@ async function buildContextClient(
467
496
  const unitId = `context-pkg:${pkg.name}:${client.name}`;
468
497
  const outDir = join(pkg.path, "dist", client.name);
469
498
 
499
+ // Per-target entry: the browser dist is built from the CLIENT entry
500
+ // (index.client.ts if present, else index.ts) so dist/browser reflects exactly
501
+ // what ships to the browser. Non-browser clients keep the base entry.
502
+ const entry =
503
+ client.target === "browser" ? pkg.clientEntrypoint : pkg.entrypoint;
504
+
470
505
  const inputHash = sha256OfJson({
471
506
  src: pkgSourceHash(pkg),
472
507
  pkg: pkg.packageJson,
@@ -474,6 +509,7 @@ async function buildContextClient(
474
509
  client: client.name,
475
510
  target: client.target,
476
511
  defines: client.defines,
512
+ entry: relative(pkg.path, entry),
477
513
  });
478
514
 
479
515
  if (!noCache && isCacheHit(cache, unitId, inputHash, [join(outDir, "main", "index.js")])) {
@@ -495,7 +531,7 @@ async function buildContextClient(
495
531
  const externals = [...peerDeps, ...Object.keys(allDeps)];
496
532
 
497
533
  const result = await Bun.build({
498
- entrypoints: [pkg.entrypoint],
534
+ entrypoints: [entry],
499
535
  outdir: join(outDir, "main"),
500
536
  target: client.target,
501
537
  format: "esm",
@@ -522,9 +558,9 @@ async function buildContextClient(
522
558
  .join("\n");
523
559
 
524
560
  const declResult = await buildTypeDeclarations(
525
- [pkg.entrypoint],
561
+ [entry],
526
562
  outDir,
527
- dirname(pkg.entrypoint),
563
+ dirname(entry),
528
564
  globalsContent,
529
565
  );
530
566
 
@@ -658,7 +694,9 @@ export async function buildServerApp(
658
694
  // Every workspace package name → its SOURCE entry. Spans ALL packages, not
659
695
  // just context ones — a context package imports non-context workspace libs
660
696
  // (e.g. content-core) that must inline from source too.
661
- const srcByName = new Map(packages.map((p) => [p.name, p.entrypoint]));
697
+ // SERVER bundle resolves every `@pkg` to its server entry (index.server.ts if
698
+ // present, else index.ts) via workspaceSourcePlugin — bypassing package.json.
699
+ const srcByName = new Map(packages.map((p) => [p.name, p.serverEntrypoint]));
662
700
 
663
701
  // External = framework peers + every non-workspace npm dep any package
664
702
  // declares. NOT bundled; resolved at runtime from /app/node_modules (the
@@ -680,7 +718,13 @@ export async function buildServerApp(
680
718
  const unitId = "server-app";
681
719
  const inputHash = sha256OfJson({
682
720
  members: packages
683
- .map((p) => ({ name: p.name, src: pkgSourceHash(p) }))
721
+ .map((p) => ({
722
+ name: p.name,
723
+ src: pkgSourceHash(p),
724
+ // Which file is the server entry — invalidate if the selection changes
725
+ // (e.g. an index.server.ts is added/removed) even without other src edits.
726
+ serverEntry: relative(p.path, p.serverEntrypoint),
727
+ }))
684
728
  .sort((a, b) => a.name.localeCompare(b.name)),
685
729
  contexts: contexts.map((p) => p.name).sort(),
686
730
  external: [...external].sort(),
@@ -1118,13 +1162,13 @@ export async function buildBrowserApp(
1118
1162
 
1119
1163
  // Cache key spans every package's source plus the build config that
1120
1164
  // matters. If anything changes, full rebuild.
1121
- const allMembers: { name: string; group: string; srcHash: string }[] = [];
1165
+ const allMembers: { name: string; group: string; srcHash: string; clientEntry: string }[] = [];
1122
1166
  for (const m of publicMembers) {
1123
- allMembers.push({ name: m.pkg.name, group: "public", srcHash: pkgSourceHash(m.pkg) });
1167
+ allMembers.push({ name: m.pkg.name, group: "public", srcHash: pkgSourceHash(m.pkg), clientEntry: relative(m.pkg.path, m.pkg.clientEntrypoint) });
1124
1168
  }
1125
1169
  for (const g of protectedGroups) {
1126
1170
  for (const m of g.members) {
1127
- allMembers.push({ name: m.pkg.name, group: g.name, srcHash: pkgSourceHash(m.pkg) });
1171
+ allMembers.push({ name: m.pkg.name, group: g.name, srcHash: pkgSourceHash(m.pkg), clientEntry: relative(m.pkg.path, m.pkg.clientEntrypoint) });
1128
1172
  }
1129
1173
  }
1130
1174
  const inputHash = sha256OfJson({
@@ -1142,7 +1186,8 @@ export async function buildBrowserApp(
1142
1186
  externals: federated ? [...SHELL_EXTERNALS] : [],
1143
1187
  // Podbij przy zmianie SEMANTYKI builda (pluginy/flagi), której nie widać
1144
1188
  // w źródłach — inaczej cache odda bundle zbudowany starą logiką.
1145
- builderRev: 3,
1189
+ // 4: client-entry (index.client.ts) + workspaceClientSourcePlugin.
1190
+ builderRev: 4,
1146
1191
  });
1147
1192
 
1148
1193
  // Tryb stats zawsze buduje od nowa — cache-hit pominąłby analizę i nie
@@ -1235,6 +1280,18 @@ export async function buildBrowserApp(
1235
1280
  for (const g of protectedGroups)
1236
1281
  for (const m of g.members) allMemberPkgs.set(m.pkg.name, m.pkg);
1237
1282
 
1283
+ // Client-entry override: only packages that DECLARE `index.client.ts`
1284
+ // (clientEntrypoint !== entrypoint) are redirected to their client source.
1285
+ // Everything else — packages without a variant, framework peers, npm — misses
1286
+ // the map and resolves natively (package.json `exports.browser`, e.g. the
1287
+ // per-pkg dist/browser). Empty map ⇒ identical to today's behaviour.
1288
+ const clientByName = new Map<string, string>();
1289
+ for (const pkg of allMemberPkgs.values()) {
1290
+ if (pkg.clientEntrypoint !== pkg.entrypoint) {
1291
+ clientByName.set(pkg.name, pkg.clientEntrypoint);
1292
+ }
1293
+ }
1294
+
1238
1295
  const patchedPkgJsons: { path: string; original: string }[] = [];
1239
1296
  for (const pkg of allMemberPkgs.values()) {
1240
1297
  const pkgJsonPath = join(pkg.path, "package.json");
@@ -1275,6 +1332,11 @@ export async function buildBrowserApp(
1275
1332
  // singletonów React/rejestru z hostem federacji.
1276
1333
  external: federated ? [...SHELL_EXTERNALS] : [],
1277
1334
  plugins: [
1335
+ // Client-entry: `@pkg` z wariantem index.client.ts → jego source. MUSI
1336
+ // być pierwszy (onResolve first-wins), ale mapa zawiera tylko pakiety
1337
+ // workspace z wariantem — react/@arcote.tech/*/npm i pakiety bez wariantu
1338
+ // miss → null → natywna rezolucja / external niżej. Zero regresji.
1339
+ ...(clientByName.size > 0 ? [workspaceSourcePlugin(clientByName)] : []),
1278
1340
  // Federated: singleReactPlugin MUSI zniknąć (jego onResolve wygrałby z
1279
1341
  // `external` → React zbundlowany; singleton daje import mapa). Ale
1280
1342
  // jsxDevShimPlugin ZOSTAJE (Bun i tak emituje jsxDEV) — jego shim jest
@@ -16,9 +16,17 @@ import type { WorkspacePackage } from "./module-builder";
16
16
  // w PUBLICZNYM bundlu przeglądarki. Seedy czyta wyłącznie host (`runSeeds()`),
17
17
  // po stronie klienta nie mają żadnego zastosowania — czysty wyciek.
18
18
  //
19
- // Gejt wywołania to WARUNEK KONIECZNY (ten guard go egzekwuje), ale to, gdzie
20
- // fizycznie leży treść seeda, decyduje czy bajty realnie znikają. Zweryfikowane
21
- // empirycznie (migracja ndt+bmf, grep bundla klienta):
19
+ // KANONICZNY WZORZEC (rekomendowany): trzymaj seedy w `index.server.ts` pakietu.
20
+ // Build serwera rozwiązuje `@pkg` do `index.server.ts`, a build przeglądarki go
21
+ // nigdy nie importuje — seedy są izolowane FIZYCZNIE (zero DCE, zero ternary,
22
+ // zero szansy na regresję z nieczystym top-level modułu seed). Guard POMIJA pliki
23
+ // `*.server.ts`, więc `.withSeeds(...)` nie wymaga tam gejtu. Patrz discoverPackages
24
+ // / buildServerApp (module-builder.ts).
25
+ //
26
+ // ŚCIEŻKA LEGACY (pliki współdzielone klient+serwer): gejt wywołania to WARUNEK
27
+ // KONIECZNY (ten guard go egzekwuje), ale to, gdzie fizycznie leży treść seeda,
28
+ // decyduje czy bajty realnie znikają. Zweryfikowane empirycznie (migracja ndt+bmf,
29
+ // grep bundla klienta):
22
30
  //
23
31
  // // mały seed — inline w gejtowanym literale (ZAWSZE działa: constant-fold):
24
32
  // .seedWith(ONLY_SERVER ? [ { /* wiersze */ } ] : [])
@@ -63,7 +71,16 @@ function walkTsFiles(dir: string, out: string[]): void {
63
71
  if (entry.isDirectory()) {
64
72
  if (entry.name === "__tests__") continue;
65
73
  walkTsFiles(abs, out);
66
- } else if (entry.isFile() && /\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
74
+ } else if (
75
+ entry.isFile() &&
76
+ /\.(ts|tsx)$/.test(entry.name) &&
77
+ !entry.name.endsWith(".d.ts") &&
78
+ // `*.server.ts(x)` (w tym index.server.ts) NIE trafiają do bundla klienta —
79
+ // build serwera rozwiązuje `@pkg` do index.server.ts, a build przeglądarki
80
+ // nigdy go nie importuje. Seedy w tych plikach są izolowane fizycznie, więc
81
+ // `.withSeeds(...)` nie wymaga tam gejtu `ONLY_SERVER`.
82
+ !/\.server\.tsx?$/.test(entry.name)
83
+ ) {
67
84
  out.push(abs);
68
85
  }
69
86
  }